@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.
- package/CHANGELOG.md +434 -0
- package/dist/components/Accordion/Accordion.css +13 -0
- package/dist/components/Accordion/Accordion.d.ts +3 -0
- package/dist/components/Accordion/Accordion.js +1 -0
- package/dist/components/AppSubheader/AppSubheader.css +10 -1
- package/dist/components/AttachmentCard/AttachmentCard.css +10 -8
- package/dist/components/AttachmentCard/AttachmentCard.d.ts +3 -0
- package/dist/components/AttachmentCard/AttachmentCard.js +8 -1
- package/dist/components/Badge/Badge.css +14 -1
- package/dist/components/Badge/Badge.d.ts +11 -2
- package/dist/components/Badge/Badge.js +3 -0
- package/dist/components/Calendar/Calendar.d.ts +7 -6
- package/dist/components/Coachmark/Coachmark.css +10 -0
- package/dist/components/Coachmark/Coachmark.d.ts +12 -0
- package/dist/components/Coachmark/Coachmark.js +46 -5
- package/dist/components/ColorPicker/ColorPicker.css +8 -1
- package/dist/components/ColorPicker/ColorPicker.d.ts +2 -2
- package/dist/components/DataTable/DataTable.css +174 -0
- package/dist/components/DataTable/DataTable.d.ts +63 -2
- package/dist/components/DataTable/DataTable.js +278 -6
- package/dist/components/DescriptionList/DescriptionList.css +14 -0
- package/dist/components/DescriptionList/DescriptionList.d.ts +3 -0
- package/dist/components/DescriptionList/DescriptionList.js +1 -0
- package/dist/components/EmptyState/EmptyState.d.ts +4 -2
- package/dist/components/EmptyState/EmptyState.js +2 -2
- package/dist/components/Input/Input.css +21 -0
- package/dist/components/Input/Input.d.ts +7 -0
- package/dist/components/Input/Input.js +2 -1
- package/dist/components/List/ListItem.d.ts +14 -2
- package/dist/components/List/ListItem.js +3 -2
- package/dist/components/RichTextEditor/RichTextEditor.css +47 -0
- package/dist/components/RichTextEditor/RichTextEditor.d.ts +22 -8
- package/dist/components/RichTextEditor/RichTextEditor.js +460 -3
- package/dist/components/SegmentedControl/SegmentedControl.css +24 -0
- package/dist/components/SegmentedControl/SegmentedControl.d.ts +2 -0
- package/dist/components/SegmentedControl/SegmentedControl.js +4 -1
- package/dist/components/Select/Select.css +94 -0
- package/dist/components/Select/Select.d.ts +26 -4
- package/dist/components/Select/Select.js +210 -35
- package/dist/components/StatCard/StatCard.css +65 -0
- package/dist/components/StatCard/StatCard.d.ts +9 -0
- package/dist/components/StatCard/StatCard.js +10 -1
- package/dist/components/Toast/Toast.css +32 -0
- package/dist/components/Toast/Toast.d.ts +18 -0
- package/dist/components/Toast/Toast.js +14 -1
- package/dist/components/Toast/ToastStack.js +8 -1
- package/dist/teste/AccordionTituloNeutro.test.d.ts +1 -0
- package/dist/teste/AccordionTituloNeutro.test.js +23 -0
- package/dist/teste/AppSubheaderRegiao.test.d.ts +1 -0
- package/dist/teste/AppSubheaderRegiao.test.js +13 -0
- package/dist/teste/AttachmentCardLista.test.d.ts +1 -0
- package/dist/teste/AttachmentCardLista.test.js +49 -0
- package/dist/teste/AttachmentCardSm.test.d.ts +1 -0
- package/dist/teste/AttachmentCardSm.test.js +26 -0
- package/dist/teste/BadgeNeutroColado.test.d.ts +1 -0
- package/dist/teste/BadgeNeutroColado.test.js +11 -0
- package/dist/teste/BadgeTag.test.js +13 -0
- package/dist/teste/CalendarCabecalho.test.d.ts +1 -0
- package/dist/teste/CalendarCabecalho.test.js +11 -0
- package/dist/teste/CoachmarkSemVeu.test.d.ts +1 -0
- package/dist/teste/CoachmarkSemVeu.test.js +85 -0
- package/dist/teste/ColorPickerXs.test.d.ts +1 -0
- package/dist/teste/ColorPickerXs.test.js +21 -0
- package/dist/teste/DataTableArvoreEVariantes.test.d.ts +1 -0
- package/dist/teste/DataTableArvoreEVariantes.test.js +190 -0
- package/dist/teste/DescriptionListCompacta.test.d.ts +1 -0
- package/dist/teste/DescriptionListCompacta.test.js +22 -0
- package/dist/teste/EmptyStateTituloRico.test.d.ts +1 -0
- package/dist/teste/EmptyStateTituloRico.test.js +23 -0
- package/dist/teste/InputAgrupado.test.d.ts +1 -0
- package/dist/teste/InputAgrupado.test.js +24 -0
- package/dist/teste/ListItemTooltipStandalone.test.d.ts +1 -0
- package/dist/teste/ListItemTooltipStandalone.test.js +39 -0
- package/dist/teste/RichTextEditorAlinhaImagemTabela.test.d.ts +1 -0
- package/dist/teste/RichTextEditorAlinhaImagemTabela.test.js +212 -0
- package/dist/teste/RichTextEditorEstiloDeTexto.test.d.ts +1 -0
- package/dist/teste/RichTextEditorEstiloDeTexto.test.js +120 -0
- package/dist/teste/SegmentedControlContador.test.d.ts +1 -0
- package/dist/teste/SegmentedControlContador.test.js +39 -0
- package/dist/teste/SelectAbertoControladoEFlutuante.test.d.ts +1 -0
- package/dist/teste/SelectAbertoControladoEFlutuante.test.js +145 -0
- package/dist/teste/SelectFiltroCorETamanho.test.d.ts +1 -0
- package/dist/teste/SelectFiltroCorETamanho.test.js +77 -0
- package/dist/teste/StatCardSecundariaETom.test.d.ts +1 -0
- package/dist/teste/StatCardSecundariaETom.test.js +45 -0
- package/dist/teste/ToastAcaoEDetalhes.test.d.ts +1 -0
- package/dist/teste/ToastAcaoEDetalhes.test.js +94 -0
- package/dist/utils/html-seguro.d.ts +19 -1
- package/dist/utils/html-seguro.js +113 -2
- package/dist/utils/toast.d.ts +10 -1
- package/dist/utils/toast.js +2 -1
- package/package.json +2 -2
- package/src/teste/AccordionTituloNeutro.test.tsx +25 -0
- package/src/teste/AppSubheaderRegiao.test.tsx +17 -0
- package/src/teste/AttachmentCardLista.test.tsx +53 -0
- package/src/teste/AttachmentCardSm.test.tsx +29 -0
- package/src/teste/BadgeNeutroColado.test.tsx +15 -0
- package/src/teste/BadgeTag.test.tsx +21 -0
- package/src/teste/CalendarCabecalho.test.tsx +11 -0
- package/src/teste/CoachmarkSemVeu.test.tsx +101 -0
- package/src/teste/ColorPickerXs.test.tsx +22 -0
- package/src/teste/DataTableArvoreEVariantes.test.tsx +240 -0
- package/src/teste/DescriptionListCompacta.test.tsx +24 -0
- package/src/teste/EmptyStateTituloRico.test.tsx +34 -0
- package/src/teste/InputAgrupado.test.tsx +26 -0
- package/src/teste/ListItemTooltipStandalone.test.tsx +64 -0
- package/src/teste/RichTextEditorAlinhaImagemTabela.test.tsx +238 -0
- package/src/teste/RichTextEditorEstiloDeTexto.test.tsx +137 -0
- package/src/teste/SegmentedControlContador.test.tsx +43 -0
- package/src/teste/SelectAbertoControladoEFlutuante.test.tsx +180 -0
- package/src/teste/SelectFiltroCorETamanho.test.tsx +90 -0
- package/src/teste/StatCardSecundariaETom.test.tsx +51 -0
- package/src/teste/ToastAcaoEDetalhes.test.tsx +109 -0
- package/src/utils/html-seguro.ts +105 -2
- package/src/utils/toast.ts +12 -2
package/src/utils/html-seguro.ts
CHANGED
|
@@ -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
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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
|
};
|
package/src/utils/toast.ts
CHANGED
|
@@ -22,7 +22,13 @@ export interface ToastOptions {
|
|
|
22
22
|
description?: string;
|
|
23
23
|
/** Variante semântica @default 'info' */
|
|
24
24
|
variant?: ToastVariant;
|
|
25
|
-
/**
|
|
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
|
-
|
|
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
|