@1doc/1ds-react 2.9.0 → 2.10.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.
Files changed (115) hide show
  1. package/CHANGELOG.md +434 -0
  2. package/dist/components/Accordion/Accordion.css +13 -0
  3. package/dist/components/Accordion/Accordion.d.ts +3 -0
  4. package/dist/components/Accordion/Accordion.js +1 -0
  5. package/dist/components/AppSubheader/AppSubheader.css +10 -1
  6. package/dist/components/AttachmentCard/AttachmentCard.css +10 -8
  7. package/dist/components/AttachmentCard/AttachmentCard.d.ts +3 -0
  8. package/dist/components/AttachmentCard/AttachmentCard.js +8 -1
  9. package/dist/components/Badge/Badge.css +14 -1
  10. package/dist/components/Badge/Badge.d.ts +11 -2
  11. package/dist/components/Badge/Badge.js +3 -0
  12. package/dist/components/Calendar/Calendar.d.ts +7 -6
  13. package/dist/components/Coachmark/Coachmark.css +10 -0
  14. package/dist/components/Coachmark/Coachmark.d.ts +12 -0
  15. package/dist/components/Coachmark/Coachmark.js +46 -5
  16. package/dist/components/ColorPicker/ColorPicker.css +8 -1
  17. package/dist/components/ColorPicker/ColorPicker.d.ts +2 -2
  18. package/dist/components/DataTable/DataTable.css +174 -0
  19. package/dist/components/DataTable/DataTable.d.ts +63 -2
  20. package/dist/components/DataTable/DataTable.js +278 -6
  21. package/dist/components/DescriptionList/DescriptionList.css +14 -0
  22. package/dist/components/DescriptionList/DescriptionList.d.ts +3 -0
  23. package/dist/components/DescriptionList/DescriptionList.js +1 -0
  24. package/dist/components/EmptyState/EmptyState.d.ts +4 -2
  25. package/dist/components/EmptyState/EmptyState.js +2 -2
  26. package/dist/components/Input/Input.css +21 -0
  27. package/dist/components/Input/Input.d.ts +7 -0
  28. package/dist/components/Input/Input.js +2 -1
  29. package/dist/components/List/ListItem.d.ts +14 -2
  30. package/dist/components/List/ListItem.js +3 -2
  31. package/dist/components/RichTextEditor/RichTextEditor.css +47 -0
  32. package/dist/components/RichTextEditor/RichTextEditor.d.ts +22 -8
  33. package/dist/components/RichTextEditor/RichTextEditor.js +460 -3
  34. package/dist/components/SegmentedControl/SegmentedControl.css +24 -0
  35. package/dist/components/SegmentedControl/SegmentedControl.d.ts +2 -0
  36. package/dist/components/SegmentedControl/SegmentedControl.js +4 -1
  37. package/dist/components/Select/Select.css +94 -0
  38. package/dist/components/Select/Select.d.ts +26 -4
  39. package/dist/components/Select/Select.js +210 -35
  40. package/dist/components/StatCard/StatCard.css +65 -0
  41. package/dist/components/StatCard/StatCard.d.ts +9 -0
  42. package/dist/components/StatCard/StatCard.js +10 -1
  43. package/dist/components/Toast/Toast.css +32 -0
  44. package/dist/components/Toast/Toast.d.ts +18 -0
  45. package/dist/components/Toast/Toast.js +14 -1
  46. package/dist/components/Toast/ToastStack.js +8 -1
  47. package/dist/teste/AccordionTituloNeutro.test.d.ts +1 -0
  48. package/dist/teste/AccordionTituloNeutro.test.js +23 -0
  49. package/dist/teste/AppSubheaderRegiao.test.d.ts +1 -0
  50. package/dist/teste/AppSubheaderRegiao.test.js +13 -0
  51. package/dist/teste/AttachmentCardLista.test.d.ts +1 -0
  52. package/dist/teste/AttachmentCardLista.test.js +49 -0
  53. package/dist/teste/AttachmentCardSm.test.d.ts +1 -0
  54. package/dist/teste/AttachmentCardSm.test.js +26 -0
  55. package/dist/teste/BadgeNeutroColado.test.d.ts +1 -0
  56. package/dist/teste/BadgeNeutroColado.test.js +11 -0
  57. package/dist/teste/BadgeTag.test.js +13 -0
  58. package/dist/teste/CalendarCabecalho.test.d.ts +1 -0
  59. package/dist/teste/CalendarCabecalho.test.js +11 -0
  60. package/dist/teste/CoachmarkSemVeu.test.d.ts +1 -0
  61. package/dist/teste/CoachmarkSemVeu.test.js +85 -0
  62. package/dist/teste/ColorPickerXs.test.d.ts +1 -0
  63. package/dist/teste/ColorPickerXs.test.js +21 -0
  64. package/dist/teste/DataTableArvoreEVariantes.test.d.ts +1 -0
  65. package/dist/teste/DataTableArvoreEVariantes.test.js +190 -0
  66. package/dist/teste/DescriptionListCompacta.test.d.ts +1 -0
  67. package/dist/teste/DescriptionListCompacta.test.js +22 -0
  68. package/dist/teste/EmptyStateTituloRico.test.d.ts +1 -0
  69. package/dist/teste/EmptyStateTituloRico.test.js +23 -0
  70. package/dist/teste/InputAgrupado.test.d.ts +1 -0
  71. package/dist/teste/InputAgrupado.test.js +24 -0
  72. package/dist/teste/ListItemTooltipStandalone.test.d.ts +1 -0
  73. package/dist/teste/ListItemTooltipStandalone.test.js +39 -0
  74. package/dist/teste/RichTextEditorAlinhaImagemTabela.test.d.ts +1 -0
  75. package/dist/teste/RichTextEditorAlinhaImagemTabela.test.js +212 -0
  76. package/dist/teste/RichTextEditorEstiloDeTexto.test.d.ts +1 -0
  77. package/dist/teste/RichTextEditorEstiloDeTexto.test.js +120 -0
  78. package/dist/teste/SegmentedControlContador.test.d.ts +1 -0
  79. package/dist/teste/SegmentedControlContador.test.js +39 -0
  80. package/dist/teste/SelectAbertoControladoEFlutuante.test.d.ts +1 -0
  81. package/dist/teste/SelectAbertoControladoEFlutuante.test.js +145 -0
  82. package/dist/teste/SelectFiltroCorETamanho.test.d.ts +1 -0
  83. package/dist/teste/SelectFiltroCorETamanho.test.js +77 -0
  84. package/dist/teste/StatCardSecundariaETom.test.d.ts +1 -0
  85. package/dist/teste/StatCardSecundariaETom.test.js +45 -0
  86. package/dist/teste/ToastAcaoEDetalhes.test.d.ts +1 -0
  87. package/dist/teste/ToastAcaoEDetalhes.test.js +94 -0
  88. package/dist/utils/html-seguro.d.ts +19 -1
  89. package/dist/utils/html-seguro.js +113 -2
  90. package/dist/utils/toast.d.ts +10 -1
  91. package/dist/utils/toast.js +2 -1
  92. package/package.json +2 -2
  93. package/src/teste/AccordionTituloNeutro.test.tsx +25 -0
  94. package/src/teste/AppSubheaderRegiao.test.tsx +17 -0
  95. package/src/teste/AttachmentCardLista.test.tsx +53 -0
  96. package/src/teste/AttachmentCardSm.test.tsx +29 -0
  97. package/src/teste/BadgeNeutroColado.test.tsx +15 -0
  98. package/src/teste/BadgeTag.test.tsx +21 -0
  99. package/src/teste/CalendarCabecalho.test.tsx +11 -0
  100. package/src/teste/CoachmarkSemVeu.test.tsx +101 -0
  101. package/src/teste/ColorPickerXs.test.tsx +22 -0
  102. package/src/teste/DataTableArvoreEVariantes.test.tsx +240 -0
  103. package/src/teste/DescriptionListCompacta.test.tsx +24 -0
  104. package/src/teste/EmptyStateTituloRico.test.tsx +34 -0
  105. package/src/teste/InputAgrupado.test.tsx +26 -0
  106. package/src/teste/ListItemTooltipStandalone.test.tsx +64 -0
  107. package/src/teste/RichTextEditorAlinhaImagemTabela.test.tsx +238 -0
  108. package/src/teste/RichTextEditorEstiloDeTexto.test.tsx +137 -0
  109. package/src/teste/SegmentedControlContador.test.tsx +43 -0
  110. package/src/teste/SelectAbertoControladoEFlutuante.test.tsx +180 -0
  111. package/src/teste/SelectFiltroCorETamanho.test.tsx +90 -0
  112. package/src/teste/StatCardSecundariaETom.test.tsx +51 -0
  113. package/src/teste/ToastAcaoEDetalhes.test.tsx +109 -0
  114. package/src/utils/html-seguro.ts +105 -2
  115. package/src/utils/toast.ts +12 -2
@@ -5,14 +5,20 @@
5
5
  * fora (um valor guardado, um texto colado) pode trazer `<img onerror>`,
6
6
  * `<a href="javascript:...">`, `<script>`, `style` e classes de outro site. Este
7
7
  * módulo reduz o HTML ao que o editor sabe escrever: negrito, itálico,
8
- * sublinhado, tachado, listas, parágrafos, quebras, títulos, citação e link.
8
+ * sublinhado, tachado, listas, parágrafos, quebras, títulos, citação, link,
9
+ * estilo de texto por token (`color`, `font-size`, `font-family` em `<span>`),
10
+ * alinhamento de bloco (`text-align`), imagem (`http(s)`, caminho ou `data:image`
11
+ * em PNG, JPEG, GIF e WebP) e tabela simples.
9
12
  *
10
13
  * Roda no navegador (precisa de DOM). Dentro de um `<template>` o HTML é inerte:
11
14
  * nada executa e nenhuma imagem carrega enquanto se higieniza. Sem DOM
12
15
  * (servidor), devolve o texto escapado.
13
16
  */
14
17
 
15
- const PERMITIDAS = ["A", "B", "STRONG", "I", "EM", "U", "S", "STRIKE", "DEL", "UL", "OL", "LI", "P", "BR", "DIV", "SPAN", "H1", "H2", "H3", "H4", "BLOCKQUOTE"];
18
+ const PERMITIDAS = ["A", "B", "STRONG", "I", "EM", "U", "S", "STRIKE", "DEL", "UL", "OL", "LI", "P", "BR", "DIV", "SPAN", "H1", "H2", "H3", "H4", "BLOCKQUOTE", "IMG", "TABLE", "THEAD", "TBODY", "TR", "TH", "TD"];
19
+ /** Blocos que aceitam `text-align`. */
20
+ const ALINHAVEIS = ["P", "DIV", "H1", "H2", "H3", "H4", "LI", "BLOCKQUOTE", "TH", "TD"];
21
+ const ALINHAMENTOS = ["left", "center", "right", "justify"];
16
22
  const DESCARTADAS = ["SCRIPT", "STYLE", "IFRAME", "OBJECT", "EMBED", "LINK", "META", "TEMPLATE", "NOSCRIPT", "SVG", "MATH", "FORM", "INPUT", "BUTTON", "TEXTAREA", "SELECT"];
17
23
  const escapar = (texto: string): string => texto.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
18
24
 
@@ -41,6 +47,66 @@ export const normalizarEndereco = (bruto: string): string => {
41
47
  if (/^[a-z][a-z0-9+.-]*:/i.test(semControle)) return "";
42
48
  return "https://" + texto;
43
49
  };
50
+
51
+ /** Cor, tamanho e família de fonte só valem como TOKEN do DS: `var(--umds-...)`. Cor em hex, rgb ou nome sai. */
52
+ const COR_TOKEN = /^var\(--umds-(?:primitive|semantic)-[a-z0-9-]+\)$/;
53
+ const TAMANHO_TOKEN = /^var\(--umds-primitive-font-size-[a-z0-9]+\)$/;
54
+ const FAMILIA_TOKEN = /^var\(--umds-(?:primitive|semantic)-font-family-(?:base|mono)\)$/;
55
+
56
+ /**
57
+ * Declaração de estilo de texto que o editor aceita: `color`, `font-size` e `font-family`,
58
+ * cada uma só com um token do DS. Devolve a declaração limpa ("color: var(--x);") ou "".
59
+ */
60
+ export const estiloDeTexto = (propriedade: string, valor: string): string => {
61
+ const prop = String(propriedade || "").trim().toLowerCase();
62
+ const val = String(valor || "").trim();
63
+ if (prop === "color" && COR_TOKEN.test(val)) return "color: " + val + ";";
64
+ if (prop === "font-size" && TAMANHO_TOKEN.test(val)) return "font-size: " + val + ";";
65
+ if (prop === "font-family" && FAMILIA_TOKEN.test(val)) return "font-family: " + val + ";";
66
+ return "";
67
+ };
68
+
69
+ /** As declarações válidas de um atributo `style` (só color, font-size e font-family por token). */
70
+ const estiloDoSpan = (estilo: string): string => {
71
+ const partes = String(estilo || "").split(";");
72
+ let saida = "";
73
+ for (let i = 0; i < partes.length; i++) {
74
+ const corte = partes[i].indexOf(":");
75
+ if (corte === -1) continue;
76
+ const decl = estiloDeTexto(partes[i].slice(0, corte), partes[i].slice(corte + 1));
77
+ if (decl) saida += (saida ? " " : "") + decl;
78
+ }
79
+ return saida;
80
+ };
81
+
82
+ /**
83
+ * Endereço de imagem que o editor aceita: `http(s)`, caminho (`/x`, `./x`, `../x`) ou
84
+ * `data:image` em base64 de PNG, JPEG, GIF ou WebP (SVG fica de fora: carrega script).
85
+ * Devolve o endereço limpo, ou "" se for perigoso ou vazio.
86
+ */
87
+ export const imagemSegura = (bruto: string): string => {
88
+ const texto = (bruto || "").trim();
89
+ if (!texto) return "";
90
+ const semControle = texto.replace(/[\u0000-\u001f\u007f\s]+/g, "");
91
+ if (/^data:image\/(png|jpe?g|gif|webp);base64,[a-z0-9+/]+=*$/i.test(semControle)) return semControle;
92
+ // `//host/x` (sem esquema) carregaria de outro site sem o autor ter pedido: fica de fora.
93
+ if (/^(https?:\/\/|\/(?!\/)|\.\.?\/)/i.test(semControle)) return texto;
94
+ return "";
95
+ };
96
+
97
+ /** O `text-align` de um elemento, pelo `style` ou pelo atributo `align`: left, center, right, justify ou "". */
98
+ const alinhamentoDe = (elemento: any): string => {
99
+ const estilo = String(elemento.getAttribute("style") || "");
100
+ const achado = /(?:^|;)\s*text-align\s*:\s*([a-z]+)\s*(?:;|$)/i.exec(estilo);
101
+ const valor = (achado ? achado[1] : String(elemento.getAttribute("align") || "")).toLowerCase();
102
+ return ALINHAMENTOS.indexOf(valor) !== -1 ? valor : "";
103
+ };
104
+
105
+ /** Número inteiro de 2 a 99 em `colspan`/`rowspan`; o resto some. */
106
+ const vao = (valor: any): string => {
107
+ const n = parseInt(String(valor || ""), 10);
108
+ return n >= 2 && n <= 99 ? String(n) : "";
109
+ };
44
110
  const limparNo = (no: any, saida: any, documento: any): void => {
45
111
  const filhos = Array.prototype.slice.call(no.childNodes);
46
112
  for (let i = 0; i < filhos.length; i++) {
@@ -57,7 +123,31 @@ const limparNo = (no: any, saida: any, documento: any): void => {
57
123
  limparNo(filho, saida, documento);
58
124
  continue;
59
125
  }
126
+ if (tag === "IMG") {
127
+ // Imagem sem endereço seguro some inteira (não deixa um <img> quebrado nem carregando de fora).
128
+ const origem = imagemSegura(filho.getAttribute("src") || "");
129
+ if (!origem) continue;
130
+ const imagem = documento.createElement("img");
131
+ imagem.setAttribute("src", origem);
132
+ imagem.setAttribute("alt", String(filho.getAttribute("alt") || ""));
133
+ saida.appendChild(imagem);
134
+ continue;
135
+ }
60
136
  const novo = documento.createElement(tag.toLowerCase());
137
+ if (ALINHAVEIS.indexOf(tag) !== -1) {
138
+ const alinhamento = alinhamentoDe(filho);
139
+ if (alinhamento) novo.setAttribute("style", "text-align: " + alinhamento + ";");
140
+ }
141
+ if (tag === "SPAN") {
142
+ const estiloValido = estiloDoSpan(filho.getAttribute("style") || "");
143
+ if (estiloValido) novo.setAttribute("style", estiloValido);
144
+ }
145
+ if (tag === "TD" || tag === "TH") {
146
+ const colunas = vao(filho.getAttribute("colspan"));
147
+ const linhas = vao(filho.getAttribute("rowspan"));
148
+ if (colunas) novo.setAttribute("colspan", colunas);
149
+ if (linhas) novo.setAttribute("rowspan", linhas);
150
+ }
61
151
  if (tag === "A") {
62
152
  const href = enderecoSeguro(filho.getAttribute("href") || "");
63
153
  if (href) {
@@ -81,8 +171,21 @@ export const sanitizarHtml = (html: string): string => {
81
171
  return saida.innerHTML;
82
172
  };
83
173
 
174
+ /** A tabela vazia que o editor insere: `linhas` × `colunas` células com uma quebra, para o cursor entrar. */
175
+ export const htmlDaTabela = (linhas: number, colunas: number): string => {
176
+ let celulas = "";
177
+ for (let c = 0; c < colunas; c++) celulas += "<td><br></td>";
178
+ let corpo = "";
179
+ for (let l = 0; l < linhas; l++) corpo += "<tr>" + celulas + "</tr>";
180
+ return "<table><tbody>" + corpo + "</tbody></table><p><br></p>";
181
+ };
182
+
183
+ /** O `<img>` que o editor insere (endereço já seguro). */
184
+ export const htmlDaImagem = (endereco: string, alt: string): string => '<img src="' + escapar(endereco) + '" alt="' + escapar(alt || "") + '">';
185
+
84
186
  /** O editor está "vazio": sem texto, sem lista e sem quebra solta (o `<br>` que o navegador deixa). */
85
187
  export const htmlVazio = (html: string): boolean => {
188
+ if (/<(img|table)[\s>]/i.test(html || "")) return false;
86
189
  const texto = (html || "").replace(/<br\s*\/?>/gi, "").replace(/<\/?(div|p|span)[^>]*>/gi, "").trim();
87
190
  return texto === "";
88
191
  };
@@ -22,7 +22,13 @@ export interface ToastOptions {
22
22
  description?: string;
23
23
  /** Variante semântica @default 'info' */
24
24
  variant?: ToastVariant;
25
- /** Auto-dismiss em ms; 0 = não fecha sozinho @default 5000 */
25
+ /** Linhas de detalhe abaixo da descrição */
26
+ details?: string[];
27
+ /** Rótulo do botão de ação ("Desfazer"); só aparece com `onAction` */
28
+ actionLabel?: string;
29
+ /** Chamado ao acionar o botão de ação; depois dele o toast fecha */
30
+ onAction?: (event?: any) => void;
31
+ /** Auto-dismiss em ms; 0 = não fecha sozinho @default 5000 (0 quando há ação: dá tempo de agir, WCAG 2.2.1) */
26
32
  duration?: number;
27
33
  /** Exibe o botão de fechar @default true */
28
34
  closable?: boolean;
@@ -31,6 +37,9 @@ export interface ToastData {
31
37
  id: number;
32
38
  message: string;
33
39
  description?: string;
40
+ details?: string[];
41
+ actionLabel?: string;
42
+ onAction?: (event?: any) => void;
34
43
  variant: ToastVariant;
35
44
  duration: number;
36
45
  closable: boolean;
@@ -50,7 +59,8 @@ export const toast = (options: ToastOptions): number => {
50
59
  const id = sequence;
51
60
  items = [...items, {
52
61
  variant: "info",
53
- duration: 5000,
62
+ // Toast com ação não some antes de dar tempo de agir.
63
+ duration: options.actionLabel && options.onAction ? 0 : 5000,
54
64
  closable: true,
55
65
  ...options,
56
66
  id