@1doc/1ds-react 0.0.1 → 2.7.1
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 +667 -0
- package/README.md +138 -6
- package/package.json +43 -10
- package/src/index.ts +161 -16
- package/src/teste/AttachmentCard.test.tsx +161 -0
- package/src/teste/Chart.test.tsx +233 -0
- package/src/teste/Combobox.test.tsx +251 -0
- package/src/teste/DataTable.test.tsx +70 -0
- package/src/teste/DatePicker.test.tsx +86 -0
- package/src/teste/Drawer.test.tsx +265 -0
- package/src/teste/Dropdown.test.tsx +217 -0
- package/src/teste/Input.test.tsx +64 -0
- package/src/teste/Kanban.test.tsx +333 -0
- package/src/teste/MultiSelect.test.tsx +73 -0
- package/src/teste/Podium.test.tsx +107 -0
- package/src/teste/Popover.test.tsx +221 -0
- package/src/teste/SegmentedControl.test.tsx +183 -0
- package/src/teste/Timeline.test.tsx +100 -0
- package/src/teste/Tree.test.tsx +74 -0
- package/src/teste/setup.ts +7 -0
- package/src/utils/bandeiras.ts +22 -0
- package/src/utils/chart.ts +682 -0
- package/src/utils/density.ts +27 -0
- package/src/utils/overlay-stack.ts +26 -0
- package/src/utils/theme.ts +63 -47
- package/src/utils/toast.ts +89 -0
- package/vitest.config.ts +28 -0
- package/dist/components/Alert/Alert.css +0 -188
- package/dist/components/Alert/Alert.d.ts +0 -65
- package/dist/components/Alert/Alert.js +0 -56
- package/dist/components/Avatar/Avatar.css +0 -88
- package/dist/components/Avatar/Avatar.d.ts +0 -34
- package/dist/components/Avatar/Avatar.js +0 -16
- package/dist/components/Avatar/AvatarGroup.d.ts +0 -12
- package/dist/components/Avatar/AvatarGroup.js +0 -8
- package/dist/components/Badge/Badge.css +0 -155
- package/dist/components/Badge/Badge.d.ts +0 -46
- package/dist/components/Badge/Badge.js +0 -17
- package/dist/components/Button/Button.css +0 -199
- package/dist/components/Button/Button.d.ts +0 -23
- package/dist/components/Button/Button.js +0 -62
- package/dist/components/Checkbox/Checkbox.css +0 -122
- package/dist/components/Checkbox/Checkbox.d.ts +0 -21
- package/dist/components/Checkbox/Checkbox.js +0 -49
- package/dist/components/Divider/Divider.css +0 -81
- package/dist/components/Divider/Divider.d.ts +0 -18
- package/dist/components/Divider/Divider.js +0 -26
- package/dist/components/Grid/Grid.css +0 -154
- package/dist/components/Grid/Grid.d.ts +0 -21
- package/dist/components/Grid/Grid.js +0 -53
- package/dist/components/Icon/Icon.css +0 -41
- package/dist/components/Icon/Icon.d.ts +0 -12
- package/dist/components/Icon/Icon.js +0 -29
- package/dist/components/Input/Input.css +0 -190
- package/dist/components/Input/Input.d.ts +0 -32
- package/dist/components/Input/Input.js +0 -58
- package/dist/components/Link/Link.css +0 -76
- package/dist/components/Link/Link.d.ts +0 -15
- package/dist/components/Link/Link.js +0 -19
- package/dist/components/Logo/Logo.css +0 -32
- package/dist/components/Logo/Logo.d.ts +0 -10
- package/dist/components/Logo/Logo.js +0 -16
- package/dist/components/Modal/Modal.css +0 -155
- package/dist/components/Modal/Modal.d.ts +0 -36
- package/dist/components/Modal/Modal.js +0 -55
- package/dist/components/Radio/Radio.css +0 -114
- package/dist/components/Radio/Radio.d.ts +0 -19
- package/dist/components/Radio/Radio.js +0 -39
- package/dist/components/Select/Select.css +0 -291
- package/dist/components/Select/Select.d.ts +0 -38
- package/dist/components/Select/Select.js +0 -246
- package/dist/components/Spinner/Spinner.css +0 -60
- package/dist/components/Spinner/Spinner.d.ts +0 -16
- package/dist/components/Spinner/Spinner.js +0 -16
- package/dist/components/Switch/Switch.css +0 -127
- package/dist/components/Switch/Switch.d.ts +0 -18
- package/dist/components/Switch/Switch.js +0 -57
- package/dist/components/Tabs/Tab.d.ts +0 -19
- package/dist/components/Tabs/Tab.js +0 -76
- package/dist/components/Tabs/TabContent.d.ts +0 -15
- package/dist/components/Tabs/TabContent.js +0 -11
- package/dist/components/Tabs/Tabs.css +0 -124
- package/dist/components/Tabs/Tabs.d.ts +0 -20
- package/dist/components/Tabs/Tabs.js +0 -14
- package/dist/components/Tabs/TabsContent.d.ts +0 -16
- package/dist/components/Tabs/TabsContent.js +0 -12
- package/dist/components/Tabs/tabs.context.d.ts +0 -2
- package/dist/components/Tabs/tabs.context.js +0 -2
- package/dist/components/Tag/Tag.css +0 -140
- package/dist/components/Tag/Tag.d.ts +0 -63
- package/dist/components/Tag/Tag.js +0 -48
- package/dist/components/Text/Text.css +0 -1213
- package/dist/components/Text/Text.d.ts +0 -70
- package/dist/components/Text/Text.js +0 -89
- package/dist/components/Textarea/Textarea.css +0 -133
- package/dist/components/Textarea/Textarea.d.ts +0 -28
- package/dist/components/Textarea/Textarea.js +0 -72
- package/dist/index.d.ts +0 -25
- package/dist/index.js +0 -27
- package/dist/utils/theme.d.ts +0 -30
- package/dist/utils/theme.js +0 -106
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pilha global de overlays de diálogo abertos (Modal, ConfirmDialog).
|
|
3
|
+
*
|
|
4
|
+
* Motivação (simulação de adoção, time SaaS RH): Modal e ConfirmDialog
|
|
5
|
+
* escutavam Esc no document de forma independente — um único Esc com um
|
|
6
|
+
* ConfirmDialog empilhado sobre um Modal fechava os dois. Com a pilha,
|
|
7
|
+
* apenas o overlay do TOPO reage ao Esc.
|
|
8
|
+
*
|
|
9
|
+
* Singleton por bundle (estado de módulo), compartilhado entre os
|
|
10
|
+
* componentes gerados React e Vue.
|
|
11
|
+
*/
|
|
12
|
+
const stack: string[] = [];
|
|
13
|
+
|
|
14
|
+
/** Registra um overlay aberto no topo da pilha (idempotente). */
|
|
15
|
+
export const pushOverlay = (id: string): void => {
|
|
16
|
+
if (!stack.includes(id)) stack.push(id);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** Remove um overlay da pilha (ao fechar ou desmontar). */
|
|
20
|
+
export const removeOverlay = (id: string): void => {
|
|
21
|
+
const index = stack.indexOf(id);
|
|
22
|
+
if (index !== -1) stack.splice(index, 1);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** true se o overlay é o do topo — o único que deve reagir ao Esc. */
|
|
26
|
+
export const isTopOverlay = (id: string): boolean => stack.length > 0 && stack[stack.length - 1] === id
|
package/src/utils/theme.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tipos de temas
|
|
2
|
+
* Tipos de temas oficiais do Design System
|
|
3
3
|
*/
|
|
4
|
-
export type Theme =
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
export type Theme = "1doc" | "1ds-red" | "1ds-green" | "1ds-orange" | "1ds-yellow" | "1ds-purple" | "1doc-dark" | "1ds-red-dark" | "1ds-green-dark" | "1ds-orange-dark" | "1ds-yellow-dark" | "1ds-purple-dark" | "1doc-high-contrast";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Nome de tema aceito pelas APIs: um tema oficial OU um tema custom de
|
|
8
|
+
* marca própria registrado via registerTheme() (white-label).
|
|
9
|
+
* O truque `(string & {})` preserva o autocomplete dos oficiais.
|
|
10
|
+
*/
|
|
11
|
+
export type ThemeName = Theme | (string & {});
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Chave do localStorage para armazenar o tema selecionado
|
|
@@ -18,14 +20,43 @@ const THEME_STORAGE_KEY = "umds-theme";
|
|
|
18
20
|
*/
|
|
19
21
|
const DEFAULT_THEME: Theme = "1doc";
|
|
20
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Temas oficiais (fonte única para validação e getAvailableThemes)
|
|
25
|
+
*/
|
|
26
|
+
const OFFICIAL_THEMES: Theme[] = ["1doc", "1ds-red", "1ds-green", "1ds-orange", "1ds-yellow", "1ds-purple", "1doc-dark", "1ds-red-dark", "1ds-green-dark", "1ds-orange-dark", "1ds-yellow-dark", "1ds-purple-dark", "1doc-high-contrast"];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Temas custom registrados em runtime (white-label).
|
|
30
|
+
*/
|
|
31
|
+
const customThemes: string[] = [];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Registra um tema de marca própria (white-label) para que setTheme,
|
|
35
|
+
* getTheme, initTheme e onThemeChange o aceitem como válido.
|
|
36
|
+
*
|
|
37
|
+
* O CSS do tema é responsabilidade do consumidor: um arquivo com
|
|
38
|
+
* `[data-theme="<nome>"]` sobrescrevendo os tokens de tema
|
|
39
|
+
* (`--umds-theme-*`) ou os semânticos (`--umds-semantic-color-*`),
|
|
40
|
+
* carregado após o semantic.css.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* registerTheme("acme");
|
|
44
|
+
* registerTheme("acme-dark");
|
|
45
|
+
* setTheme("acme"); // agora válido e persistido
|
|
46
|
+
*/
|
|
47
|
+
export const registerTheme = (theme: string): void => {
|
|
48
|
+
if (!customThemes.includes(theme) && !OFFICIAL_THEMES.includes(theme as Theme)) {
|
|
49
|
+
customThemes.push(theme);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
21
53
|
/**
|
|
22
54
|
* Define o tema ativo no documento
|
|
23
|
-
* @param theme - Nome do tema a ser aplicado
|
|
55
|
+
* @param theme - Nome do tema a ser aplicado (oficial ou registrado)
|
|
24
56
|
*/
|
|
25
|
-
export const setTheme = (theme:
|
|
57
|
+
export const setTheme = (theme: ThemeName): void => {
|
|
26
58
|
if (typeof document !== "undefined") {
|
|
27
59
|
document.documentElement.setAttribute("data-theme", theme);
|
|
28
|
-
|
|
29
60
|
try {
|
|
30
61
|
localStorage.setItem(THEME_STORAGE_KEY, theme);
|
|
31
62
|
} catch (e) {
|
|
@@ -36,17 +67,19 @@ export const setTheme = (theme: Theme): void => {
|
|
|
36
67
|
};
|
|
37
68
|
|
|
38
69
|
/**
|
|
39
|
-
* Obtém o tema atual, considerando localStorage e preferências do sistema
|
|
70
|
+
* Obtém o tema atual, considerando localStorage e preferências do sistema.
|
|
71
|
+
* Temas custom precisam estar registrados (registerTheme) ANTES da leitura,
|
|
72
|
+
* senão o valor persistido é descartado em favor do padrão.
|
|
40
73
|
* @returns O tema atualmente selecionado
|
|
41
74
|
*/
|
|
42
|
-
export const getTheme = ():
|
|
75
|
+
export const getTheme = (): ThemeName => {
|
|
43
76
|
if (typeof window === "undefined") {
|
|
44
77
|
return DEFAULT_THEME;
|
|
45
78
|
}
|
|
46
79
|
|
|
47
80
|
// 1. Tenta buscar do localStorage
|
|
48
81
|
try {
|
|
49
|
-
const stored = localStorage.getItem(THEME_STORAGE_KEY)
|
|
82
|
+
const stored = localStorage.getItem(THEME_STORAGE_KEY);
|
|
50
83
|
if (stored && isValidTheme(stored)) {
|
|
51
84
|
return stored;
|
|
52
85
|
}
|
|
@@ -55,9 +88,7 @@ export const getTheme = (): Theme => {
|
|
|
55
88
|
}
|
|
56
89
|
|
|
57
90
|
// 2. Tenta buscar do atributo data-theme do HTML
|
|
58
|
-
const currentTheme = document.documentElement.getAttribute(
|
|
59
|
-
"data-theme",
|
|
60
|
-
) as Theme | null;
|
|
91
|
+
const currentTheme = document.documentElement.getAttribute("data-theme");
|
|
61
92
|
if (currentTheme && isValidTheme(currentTheme)) {
|
|
62
93
|
return currentTheme;
|
|
63
94
|
}
|
|
@@ -68,7 +99,9 @@ export const getTheme = (): Theme => {
|
|
|
68
99
|
|
|
69
100
|
/**
|
|
70
101
|
* Inicializa o tema ao carregar a página
|
|
71
|
-
* Deve ser chamado o mais cedo possível para evitar flash de conteúdo
|
|
102
|
+
* Deve ser chamado o mais cedo possível para evitar flash de conteúdo.
|
|
103
|
+
* White-label: chame registerTheme() antes de initTheme() para que o tema
|
|
104
|
+
* custom persistido no localStorage seja restaurado.
|
|
72
105
|
*/
|
|
73
106
|
export const initTheme = (): void => {
|
|
74
107
|
const theme = getTheme();
|
|
@@ -76,27 +109,20 @@ export const initTheme = (): void => {
|
|
|
76
109
|
};
|
|
77
110
|
|
|
78
111
|
/**
|
|
79
|
-
* Valida se uma string é um tema válido
|
|
112
|
+
* Valida se uma string é um tema válido (oficial ou registrado)
|
|
80
113
|
* @param theme - String a ser validada
|
|
81
114
|
* @returns true se o tema é válido
|
|
82
115
|
*/
|
|
83
|
-
function isValidTheme(theme: string):
|
|
84
|
-
|
|
85
|
-
"1doc",
|
|
86
|
-
"1ds-red",
|
|
87
|
-
"1ds-green",
|
|
88
|
-
"1ds-orange",
|
|
89
|
-
"1ds-yellow",
|
|
90
|
-
];
|
|
91
|
-
return validThemes.includes(theme as Theme);
|
|
116
|
+
function isValidTheme(theme: string): boolean {
|
|
117
|
+
return OFFICIAL_THEMES.includes(theme as Theme) || customThemes.includes(theme);
|
|
92
118
|
}
|
|
93
119
|
|
|
94
120
|
/**
|
|
95
|
-
* Retorna todos os temas disponíveis
|
|
121
|
+
* Retorna todos os temas disponíveis (oficiais + registrados)
|
|
96
122
|
* @returns Array com todos os temas disponíveis
|
|
97
123
|
*/
|
|
98
|
-
export const getAvailableThemes = ():
|
|
99
|
-
return [
|
|
124
|
+
export const getAvailableThemes = (): ThemeName[] => {
|
|
125
|
+
return [...OFFICIAL_THEMES, ...customThemes];
|
|
100
126
|
};
|
|
101
127
|
|
|
102
128
|
/**
|
|
@@ -104,33 +130,23 @@ export const getAvailableThemes = (): Theme[] => {
|
|
|
104
130
|
* @param callback - Função a ser executada quando o tema mudar
|
|
105
131
|
* @returns Função para remover o listener
|
|
106
132
|
*/
|
|
107
|
-
export const onThemeChange = (
|
|
108
|
-
callback: (theme: Theme) => void,
|
|
109
|
-
): (() => void) => {
|
|
133
|
+
export const onThemeChange = (callback: (theme: ThemeName) => void): (() => void) => {
|
|
110
134
|
if (typeof window === "undefined") {
|
|
111
135
|
return () => {};
|
|
112
136
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
mutation.type === "attributes" &&
|
|
118
|
-
mutation.attributeName === "data-theme"
|
|
119
|
-
) {
|
|
120
|
-
const newTheme = document.documentElement.getAttribute(
|
|
121
|
-
"data-theme",
|
|
122
|
-
) as Theme;
|
|
137
|
+
const observer = new MutationObserver(mutations => {
|
|
138
|
+
mutations.forEach(mutation => {
|
|
139
|
+
if (mutation.type === "attributes" && mutation.attributeName === "data-theme") {
|
|
140
|
+
const newTheme = document.documentElement.getAttribute("data-theme");
|
|
123
141
|
if (newTheme && isValidTheme(newTheme)) {
|
|
124
142
|
callback(newTheme);
|
|
125
143
|
}
|
|
126
144
|
}
|
|
127
145
|
});
|
|
128
146
|
});
|
|
129
|
-
|
|
130
147
|
observer.observe(document.documentElement, {
|
|
131
148
|
attributes: true,
|
|
132
|
-
attributeFilter: ["data-theme"]
|
|
149
|
+
attributeFilter: ["data-theme"]
|
|
133
150
|
});
|
|
134
|
-
|
|
135
151
|
return () => observer.disconnect();
|
|
136
|
-
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gerenciador imperativo de toasts com fila (achado 4/5 times na simulação
|
|
3
|
+
* de adoção: o Toast declarativo obrigava cada app a criar um host próprio,
|
|
4
|
+
* e mensagens em sequência se atropelavam).
|
|
5
|
+
*
|
|
6
|
+
* Uso:
|
|
7
|
+
* 1. Monte UM `<ToastStack />` na raiz do app (define a posição da pilha).
|
|
8
|
+
* 2. Dispare de qualquer lugar: `toast({ message: "Salvo!", variant: "positive" })`.
|
|
9
|
+
*
|
|
10
|
+
* O componente declarativo `<Toast />` continua existindo como primitiva
|
|
11
|
+
* para casos controlados simples.
|
|
12
|
+
*
|
|
13
|
+
* Framework-agnóstico: estado de módulo + pub/sub (singleton por bundle),
|
|
14
|
+
* consumido pelos ToastStack gerados de React e Vue.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export type ToastVariant = "positive" | "negative" | "info";
|
|
18
|
+
export interface ToastOptions {
|
|
19
|
+
/** Mensagem principal do toast */
|
|
20
|
+
message: string;
|
|
21
|
+
/** Descrição adicional abaixo da mensagem */
|
|
22
|
+
description?: string;
|
|
23
|
+
/** Variante semântica @default 'info' */
|
|
24
|
+
variant?: ToastVariant;
|
|
25
|
+
/** Auto-dismiss em ms; 0 = não fecha sozinho @default 5000 */
|
|
26
|
+
duration?: number;
|
|
27
|
+
/** Exibe o botão de fechar @default true */
|
|
28
|
+
closable?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface ToastData {
|
|
31
|
+
id: number;
|
|
32
|
+
message: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
variant: ToastVariant;
|
|
35
|
+
duration: number;
|
|
36
|
+
closable: boolean;
|
|
37
|
+
}
|
|
38
|
+
type ToastListener = (items: ToastData[]) => void;
|
|
39
|
+
let sequence = 0;
|
|
40
|
+
let items: ToastData[] = [];
|
|
41
|
+
const listeners: ToastListener[] = [];
|
|
42
|
+
const notify = (): void => {
|
|
43
|
+
const snapshot = [...items];
|
|
44
|
+
listeners.forEach(listener => listener(snapshot));
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Enfileira um toast e retorna o id (para dismiss programático). */
|
|
48
|
+
export const toast = (options: ToastOptions): number => {
|
|
49
|
+
sequence += 1;
|
|
50
|
+
const id = sequence;
|
|
51
|
+
items = [...items, {
|
|
52
|
+
variant: "info",
|
|
53
|
+
duration: 5000,
|
|
54
|
+
closable: true,
|
|
55
|
+
...options,
|
|
56
|
+
id
|
|
57
|
+
}];
|
|
58
|
+
notify();
|
|
59
|
+
return id;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** Remove um toast específico da pilha. */
|
|
63
|
+
export const dismissToast = (id: number): void => {
|
|
64
|
+
const next = items.filter(item => item.id !== id);
|
|
65
|
+
if (next.length !== items.length) {
|
|
66
|
+
items = next;
|
|
67
|
+
notify();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** Limpa a pilha inteira. */
|
|
72
|
+
export const dismissAllToasts = (): void => {
|
|
73
|
+
if (items.length === 0) return;
|
|
74
|
+
items = [];
|
|
75
|
+
notify();
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Inscreve um listener (chamado imediatamente com o estado atual).
|
|
80
|
+
* Retorna a função de desinscrição. Usado pelo ToastStack.
|
|
81
|
+
*/
|
|
82
|
+
export const subscribeToasts = (listener: ToastListener): (() => void) => {
|
|
83
|
+
listeners.push(listener);
|
|
84
|
+
listener([...items]);
|
|
85
|
+
return () => {
|
|
86
|
+
const index = listeners.indexOf(listener);
|
|
87
|
+
if (index !== -1) listeners.splice(index, 1);
|
|
88
|
+
};
|
|
89
|
+
}
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Testes unitários dos componentes GERADOS em React.
|
|
5
|
+
*
|
|
6
|
+
* Por que aqui e não no `core`: o `.lite.tsx` não roda — é fonte para o
|
|
7
|
+
* Mitosis. O que o consumidor instala é o gerado, e é dele que o teste tem de
|
|
8
|
+
* falar. Um teste que passa no core e quebra no gerado não serve.
|
|
9
|
+
*
|
|
10
|
+
* O que estes testes cobrem, e a esteira visual não: comportamento. Tecla,
|
|
11
|
+
* foco, o que o callback recebe, o que o `aria-` diz. A esteira garante que a
|
|
12
|
+
* tela não mudou; o teste garante que ela ainda FUNCIONA.
|
|
13
|
+
*/
|
|
14
|
+
export default defineConfig({
|
|
15
|
+
test: {
|
|
16
|
+
environment: "jsdom",
|
|
17
|
+
globals: true,
|
|
18
|
+
setupFiles: ["./src/teste/setup.ts"],
|
|
19
|
+
// Os testes vivem em `src/teste/`, e NÃO ao lado do componente: a pasta
|
|
20
|
+
// `src/components` é gerada pelo Mitosis, está no .gitignore e é reescrita
|
|
21
|
+
// a cada build — um teste ali não seria versionado e sumiria no build.
|
|
22
|
+
include: ["src/teste/**/*.test.tsx"],
|
|
23
|
+
// O CSS dos componentes é importado por efeito colateral (`import "./X.css"`).
|
|
24
|
+
// Sem processá-lo, o import resolve para vazio — que é o que se quer: o
|
|
25
|
+
// teste é de comportamento, e estilo quem verifica é a regressão visual.
|
|
26
|
+
css: false,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
/* ============================================
|
|
2
|
-
Alert Component
|
|
3
|
-
Tipos: neutral, success, warning, error
|
|
4
|
-
Variantes: soft, solid
|
|
5
|
-
============================================ */
|
|
6
|
-
|
|
7
|
-
.umds-alert {
|
|
8
|
-
display: flex;
|
|
9
|
-
align-items: flex-start;
|
|
10
|
-
gap: 16px;
|
|
11
|
-
padding: 16px;
|
|
12
|
-
border-radius: 8px;
|
|
13
|
-
box-sizing: border-box;
|
|
14
|
-
width: 475px;
|
|
15
|
-
max-width: calc(100vw - 32px);
|
|
16
|
-
transition: all 0.3s ease;
|
|
17
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
18
|
-
|
|
19
|
-
font-family: var(--umds-semantic-font-family-base, 'Open Sans', 'Inter', sans-serif);
|
|
20
|
-
position: fixed;
|
|
21
|
-
z-index: 9999;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/* ----------------------------------------
|
|
25
|
-
Positioning modifiers
|
|
26
|
-
---------------------------------------- */
|
|
27
|
-
.umds-alert--top-right {
|
|
28
|
-
top: 24px;
|
|
29
|
-
right: 24px;
|
|
30
|
-
}
|
|
31
|
-
.umds-alert--top-left {
|
|
32
|
-
top: 24px;
|
|
33
|
-
left: 24px;
|
|
34
|
-
}
|
|
35
|
-
.umds-alert--top-center {
|
|
36
|
-
top: 24px;
|
|
37
|
-
left: 50%;
|
|
38
|
-
transform: translateX(-50%);
|
|
39
|
-
}
|
|
40
|
-
.umds-alert--bottom-right {
|
|
41
|
-
bottom: 24px;
|
|
42
|
-
right: 24px;
|
|
43
|
-
}
|
|
44
|
-
.umds-alert--bottom-left {
|
|
45
|
-
bottom: 24px;
|
|
46
|
-
left: 24px;
|
|
47
|
-
}
|
|
48
|
-
.umds-alert--bottom-center {
|
|
49
|
-
bottom: 24px;
|
|
50
|
-
left: 50%;
|
|
51
|
-
transform: translateX(-50%);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/* ----------------------------------------
|
|
55
|
-
Internal elements
|
|
56
|
-
---------------------------------------- */
|
|
57
|
-
.umds-alert__icon-wrapper {
|
|
58
|
-
display: flex;
|
|
59
|
-
align-items: flex-start;
|
|
60
|
-
flex-shrink: 0;
|
|
61
|
-
margin-top: 2px;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.umds-alert__content {
|
|
65
|
-
display: flex;
|
|
66
|
-
flex-direction: column;
|
|
67
|
-
gap: 4px;
|
|
68
|
-
flex: 1 1 0;
|
|
69
|
-
min-width: 0;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.umds-alert__title-container {
|
|
73
|
-
display: flex;
|
|
74
|
-
width: 100%;
|
|
75
|
-
align-items: flex-start;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.umds-alert__title-text {
|
|
79
|
-
font-weight: var(--umds-primitive-font-weight-semibold, 600) !important;
|
|
80
|
-
font-size: var(--umds-primitive-font-size-xxxxs, 12px) !important;
|
|
81
|
-
margin: 0;
|
|
82
|
-
line-height: normal;
|
|
83
|
-
letter-spacing: 0.06px;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.umds-alert__description-text {
|
|
87
|
-
font-weight: var(--umds-primitive-font-weight-regular, 400) !important;
|
|
88
|
-
font-size: var(--umds-primitive-font-size-xxxxs, 12px) !important;
|
|
89
|
-
margin: 0;
|
|
90
|
-
line-height: normal;
|
|
91
|
-
letter-spacing: 0.06px;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.umds-alert__actions {
|
|
95
|
-
display: flex;
|
|
96
|
-
gap: 8px;
|
|
97
|
-
padding-top: 8px;
|
|
98
|
-
align-items: flex-start;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.umds-alert__close {
|
|
102
|
-
all: unset;
|
|
103
|
-
display: flex;
|
|
104
|
-
align-items: center;
|
|
105
|
-
justify-content: center;
|
|
106
|
-
flex-shrink: 0;
|
|
107
|
-
width: 24px;
|
|
108
|
-
height: 24px;
|
|
109
|
-
cursor: pointer;
|
|
110
|
-
border-radius: 4px;
|
|
111
|
-
transition: opacity 0.2s ease, background-color 0.2s ease;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.umds-alert__close:hover,
|
|
115
|
-
.umds-alert__close:focus-visible {
|
|
116
|
-
opacity: 0.8;
|
|
117
|
-
background-color: rgba(0, 0, 0, 0.05); /* very soft fallback highlight */
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
.umds-alert__close:focus-visible {
|
|
121
|
-
outline: 2px solid var(--umds-semantic-color-primary-pure);
|
|
122
|
-
outline-offset: 1px;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/* ----------------------------------------
|
|
126
|
-
Theme Variantes: SOFT
|
|
127
|
-
---------------------------------------- */
|
|
128
|
-
|
|
129
|
-
/* Neutral Soft */
|
|
130
|
-
.umds-alert--soft.umds-alert--neutral {
|
|
131
|
-
background-color: var(--umds-semantic-color-background-white, white);
|
|
132
|
-
color: var(--umds-semantic-color-neutral-dark-pure, #333);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/* Warning Soft (Uses info tokens on 1ds standard for warning logic from figma) */
|
|
136
|
-
.umds-alert--soft.umds-alert--warning {
|
|
137
|
-
background-color: var(--umds-semantic-color-feedback-info-light, #ffe9d9);
|
|
138
|
-
border: 1px solid var(--umds-semantic-color-feedback-info-pure, #db6816);
|
|
139
|
-
color: var(--umds-semantic-color-feedback-info-dark, #99470c);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/* Success Soft */
|
|
143
|
-
.umds-alert--soft.umds-alert--success {
|
|
144
|
-
background-color: var(--umds-semantic-color-feedback-positive-light, #cef2e1);
|
|
145
|
-
border: 1px solid var(--umds-semantic-color-feedback-positive-pure, #0f874e);
|
|
146
|
-
color: var(--umds-semantic-color-feedback-positive-dark, #0f4c30);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/* Error Soft */
|
|
150
|
-
.umds-alert--soft.umds-alert--error {
|
|
151
|
-
background-color: var(--umds-semantic-color-feedback-negative-light, #ffe6ea);
|
|
152
|
-
border: 1px solid var(--umds-semantic-color-feedback-negative-pure, #b2132e);
|
|
153
|
-
color: var(--umds-semantic-color-feedback-negative-dark, #801224);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/* ----------------------------------------
|
|
157
|
-
Theme Variantes: SOLID
|
|
158
|
-
---------------------------------------- */
|
|
159
|
-
|
|
160
|
-
/* Neutral Solid */
|
|
161
|
-
.umds-alert--solid.umds-alert--neutral {
|
|
162
|
-
background-color: var(--umds-semantic-color-neutral-dark-pure, #333333);
|
|
163
|
-
color: var(--umds-semantic-color-neutral-light-pure, #ffffff);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/* Warning Solid */
|
|
167
|
-
.umds-alert--solid.umds-alert--warning {
|
|
168
|
-
background-color: var(--umds-semantic-color-feedback-info-pure, #db6816);
|
|
169
|
-
color: var(--umds-semantic-color-neutral-light-pure, #ffffff);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/* Success Solid */
|
|
173
|
-
.umds-alert--solid.umds-alert--success {
|
|
174
|
-
background-color: var(--umds-semantic-color-feedback-positive-pure, #0f874e);
|
|
175
|
-
color: var(--umds-semantic-color-neutral-light-pure, #ffffff);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/* Error Solid */
|
|
179
|
-
.umds-alert--solid.umds-alert--error {
|
|
180
|
-
background-color: var(--umds-semantic-color-feedback-negative-pure, #b2132e);
|
|
181
|
-
color: var(--umds-semantic-color-neutral-light-pure, #ffffff);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/* Overrides for close icon on SOLID */
|
|
185
|
-
.umds-alert--solid .umds-alert__close:hover,
|
|
186
|
-
.umds-alert--solid .umds-alert__close:focus-visible {
|
|
187
|
-
background-color: rgba(255, 255, 255, 0.15);
|
|
188
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import './Alert.css';
|
|
2
|
-
/**
|
|
3
|
-
* Componente Alert é utilizado para comunicar mensagens importantes que requerem a atenção imediata do usuário.
|
|
4
|
-
* Pode indicar sucessos, avisos, erros ou informações relevantes de forma clara e destacada.
|
|
5
|
-
*/
|
|
6
|
-
export interface AlertProps {
|
|
7
|
-
/**
|
|
8
|
-
* Título do alerta.
|
|
9
|
-
*/
|
|
10
|
-
title?: string;
|
|
11
|
-
/**
|
|
12
|
-
* Propriedade obrigatória do aviso que explica do que se trata o alerta.
|
|
13
|
-
*/
|
|
14
|
-
description?: string;
|
|
15
|
-
/**
|
|
16
|
-
* Tipo/Cor semântico do alerta.
|
|
17
|
-
* @default 'neutral'
|
|
18
|
-
*/
|
|
19
|
-
type?: "neutral" | "success" | "warning" | "error";
|
|
20
|
-
/**
|
|
21
|
-
* Estilo visual ('soft' para fundos claros; 'solid' para preenchimentos escuros/cores fortes).
|
|
22
|
-
* @default 'soft'
|
|
23
|
-
*/
|
|
24
|
-
variant?: "soft" | "solid";
|
|
25
|
-
/**
|
|
26
|
-
* Posição vertical na tela para a exibição flutuante.
|
|
27
|
-
* @default 'top'
|
|
28
|
-
*/
|
|
29
|
-
verticalPosition?: "top" | "bottom";
|
|
30
|
-
/**
|
|
31
|
-
* Posição horizontal na tela para a exibição flutuante.
|
|
32
|
-
* @default 'right'
|
|
33
|
-
*/
|
|
34
|
-
horizontalPosition?: "left" | "center" | "right";
|
|
35
|
-
/**
|
|
36
|
-
* Tempo em milissegundos que o alerta ficará na tela e desaparecerá.
|
|
37
|
-
* Se 0, ou não definido, o alerta não desaparece de forma automática.
|
|
38
|
-
* @default 0
|
|
39
|
-
*/
|
|
40
|
-
time?: number;
|
|
41
|
-
/**
|
|
42
|
-
* Define ou não a visibilidade do ícone de dispensar o aviso.
|
|
43
|
-
* @default false
|
|
44
|
-
*/
|
|
45
|
-
dismissable?: boolean;
|
|
46
|
-
/**
|
|
47
|
-
* Possibilita uso de elementos HTML/Componentes JSX ou text simples no bloco de ação (links/etc).
|
|
48
|
-
*/
|
|
49
|
-
content?: any;
|
|
50
|
-
/**
|
|
51
|
-
* Slot principal no Mitosis para o custom content generalizado.
|
|
52
|
-
*/
|
|
53
|
-
children?: any;
|
|
54
|
-
/**
|
|
55
|
-
* Permite sobrescrever classes customizadas na raiz do container do alerta
|
|
56
|
-
*/
|
|
57
|
-
className?: string;
|
|
58
|
-
/**
|
|
59
|
-
* Evento acionado ao clicar para fechar o alerta
|
|
60
|
-
*/
|
|
61
|
-
onClose?: (event?: any) => void;
|
|
62
|
-
[key: string]: any;
|
|
63
|
-
}
|
|
64
|
-
declare function Alert(props: AlertProps): import("react/jsx-runtime").JSX.Element;
|
|
65
|
-
export default Alert;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import './Alert.css';
|
|
3
|
-
"use client";
|
|
4
|
-
import { useState, useEffect } from "react";
|
|
5
|
-
import Icon from "../Icon/Icon";
|
|
6
|
-
import Text from "../Text/Text";
|
|
7
|
-
function Alert(props) {
|
|
8
|
-
const [visible, setVisible] = useState(() => true);
|
|
9
|
-
const [timeoutId, setTimeoutId] = useState(() => 0);
|
|
10
|
-
function classes() {
|
|
11
|
-
return [
|
|
12
|
-
"umds-alert",
|
|
13
|
-
`umds-alert--${props.type || "neutral"}`,
|
|
14
|
-
`umds-alert--${props.variant || "soft"}`,
|
|
15
|
-
`umds-alert--${props.verticalPosition || "top"}-${props.horizontalPosition || "right"}`,
|
|
16
|
-
props.className,
|
|
17
|
-
]
|
|
18
|
-
.filter(Boolean)
|
|
19
|
-
.join(" ");
|
|
20
|
-
}
|
|
21
|
-
function iconName() {
|
|
22
|
-
const currentType = props.type || "neutral";
|
|
23
|
-
if (currentType === "success")
|
|
24
|
-
return "check-circle";
|
|
25
|
-
if (currentType === "error")
|
|
26
|
-
return "xmark-circle";
|
|
27
|
-
if (currentType === "warning")
|
|
28
|
-
return "warning-triangle";
|
|
29
|
-
return "info-circle";
|
|
30
|
-
}
|
|
31
|
-
function handleClose(event) {
|
|
32
|
-
if (event)
|
|
33
|
-
event.stopPropagation();
|
|
34
|
-
setVisible(false);
|
|
35
|
-
if (props.onClose) {
|
|
36
|
-
props.onClose(event);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
const timeProp = typeof props.time === "number" ? props.time : 0;
|
|
41
|
-
if (timeProp > 0) {
|
|
42
|
-
setTimeoutId(setTimeout(() => {
|
|
43
|
-
handleClose(null);
|
|
44
|
-
}, timeProp));
|
|
45
|
-
}
|
|
46
|
-
}, []);
|
|
47
|
-
useEffect(() => {
|
|
48
|
-
return () => {
|
|
49
|
-
if (timeoutId) {
|
|
50
|
-
clearTimeout(timeoutId);
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
}, []);
|
|
54
|
-
return (_jsx(_Fragment, { children: visible ? (_jsx(_Fragment, { children: _jsxs("div", { role: "alert", ...props, className: classes(), children: [_jsx("div", { className: "umds-alert__icon-wrapper", children: _jsx(Icon, { variant: "inherit", size: "md", name: iconName() }) }), _jsxs("div", { className: "umds-alert__content", children: [props.title ? (_jsx("div", { className: "umds-alert__title-container", children: _jsx(Text, { variant: "body2", as: "p", className: "umds-alert__title-text", children: props.title }) })) : null, props.description ? (_jsx(Text, { variant: "caption", as: "p", className: "umds-alert__description-text", children: props.description })) : null, props.content || props.children ? (_jsx("div", { className: "umds-alert__actions", children: _jsx(_Fragment, { children: props.content || "{props.content || props.children}" }) })) : null] }), props.dismissable ? (_jsx("button", { type: "button", "aria-label": "Dispensar alerta", className: "umds-alert__close", onClick: (event) => handleClose(event), children: _jsx(Icon, { name: "nav-arrow-up", variant: "inherit", size: "md" }) })) : null] }) })) : null }));
|
|
55
|
-
}
|
|
56
|
-
export default Alert;
|