@1doc/1ds-react 2.9.0 → 2.11.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 +653 -0
- package/dist/components/Accordion/Accordion.css +13 -0
- package/dist/components/Accordion/Accordion.d.ts +9 -0
- package/dist/components/Accordion/Accordion.js +18 -5
- 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 +27 -5
- package/dist/components/Badge/Badge.d.ts +20 -4
- package/dist/components/Badge/Badge.js +6 -0
- package/dist/components/Button/Button.css +5 -0
- package/dist/components/Button/Button.d.ts +3 -0
- package/dist/components/Button/Button.js +1 -0
- package/dist/components/Calendar/Calendar.css +10 -0
- package/dist/components/Calendar/Calendar.d.ts +10 -6
- package/dist/components/Calendar/Calendar.js +6 -1
- package/dist/components/Coachmark/Coachmark.css +10 -0
- package/dist/components/Coachmark/Coachmark.d.ts +14 -1
- package/dist/components/Coachmark/Coachmark.js +51 -5
- package/dist/components/ColorPicker/ColorPicker.css +8 -1
- package/dist/components/ColorPicker/ColorPicker.d.ts +2 -2
- package/dist/components/DataTable/DataTable.css +216 -0
- package/dist/components/DataTable/DataTable.d.ts +70 -2
- package/dist/components/DataTable/DataTable.js +293 -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 +30 -0
- package/dist/components/Input/Input.d.ts +10 -0
- package/dist/components/Input/Input.js +3 -1
- package/dist/components/List/ListItem.d.ts +14 -2
- package/dist/components/List/ListItem.js +3 -2
- package/dist/components/Modal/Modal.css +19 -0
- package/dist/components/Modal/Modal.d.ts +7 -0
- package/dist/components/Modal/Modal.js +1 -0
- 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 +70 -0
- package/dist/components/SegmentedControl/SegmentedControl.d.ts +5 -0
- package/dist/components/SegmentedControl/SegmentedControl.js +5 -1
- package/dist/components/Select/Select.css +122 -2
- package/dist/components/Select/Select.d.ts +33 -4
- package/dist/components/Select/Select.js +256 -35
- package/dist/components/StatCard/StatCard.css +73 -0
- package/dist/components/StatCard/StatCard.d.ts +12 -0
- package/dist/components/StatCard/StatCard.js +11 -1
- package/dist/components/Tabs/Tabs.css +11 -0
- package/dist/components/Tabs/Tabs.d.ts +7 -0
- package/dist/components/Tabs/Tabs.js +7 -1
- package/dist/components/Tag/Tag.css +3 -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/AccordionControlado.test.d.ts +1 -0
- package/dist/teste/AccordionControlado.test.js +73 -0
- 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/BadgeAttachedTomEPadding.test.d.ts +1 -0
- package/dist/teste/BadgeAttachedTomEPadding.test.js +43 -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/ButtonDensidade.test.d.ts +1 -0
- package/dist/teste/ButtonDensidade.test.js +28 -0
- package/dist/teste/CalendarCabecalho.test.d.ts +1 -0
- package/dist/teste/CalendarCabecalho.test.js +11 -0
- package/dist/teste/CalendarCompacto.test.d.ts +1 -0
- package/dist/teste/CalendarCompacto.test.js +29 -0
- package/dist/teste/CoachmarkSemVeu.test.d.ts +1 -0
- package/dist/teste/CoachmarkSemVeu.test.js +85 -0
- package/dist/teste/CoachmarkUmPasso.test.d.ts +1 -0
- package/dist/teste/CoachmarkUmPasso.test.js +34 -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/DataTableCompactaETom.test.d.ts +1 -0
- package/dist/teste/DataTableCompactaETom.test.js +69 -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/InputGhost.test.d.ts +1 -0
- package/dist/teste/InputGhost.test.js +32 -0
- package/dist/teste/ListItemTooltipStandalone.test.d.ts +1 -0
- package/dist/teste/ListItemTooltipStandalone.test.js +39 -0
- package/dist/teste/ModalProporcoes1Doc.test.d.ts +1 -0
- package/dist/teste/ModalProporcoes1Doc.test.js +32 -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/SegmentedControlSuperficie.test.d.ts +1 -0
- package/dist/teste/SegmentedControlSuperficie.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/SelectItemComEscopo.test.d.ts +1 -0
- package/dist/teste/SelectItemComEscopo.test.js +54 -0
- package/dist/teste/SelectListaFiltro.test.d.ts +1 -0
- package/dist/teste/SelectListaFiltro.test.js +102 -0
- package/dist/teste/StatCardSecundariaETom.test.d.ts +1 -0
- package/dist/teste/StatCardSecundariaETom.test.js +45 -0
- package/dist/teste/StatCardTamanhoSm.test.d.ts +1 -0
- package/dist/teste/StatCardTamanhoSm.test.js +32 -0
- package/dist/teste/TabsCompacta.test.d.ts +1 -0
- package/dist/teste/TabsCompacta.test.js +35 -0
- package/dist/teste/TagPositivoTingido.test.d.ts +1 -0
- package/dist/teste/TagPositivoTingido.test.js +54 -0
- package/dist/teste/ToastAcaoEDetalhes.test.d.ts +1 -0
- package/dist/teste/ToastAcaoEDetalhes.test.js +94 -0
- package/dist/teste/lerArquivo.d.ts +1 -0
- package/dist/teste/lerArquivo.js +12 -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 +3 -3
- package/src/teste/AccordionControlado.test.tsx +87 -0
- 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/BadgeAttachedTomEPadding.test.tsx +60 -0
- package/src/teste/BadgeNeutroColado.test.tsx +15 -0
- package/src/teste/BadgeTag.test.tsx +21 -0
- package/src/teste/ButtonDensidade.test.tsx +40 -0
- package/src/teste/CalendarCabecalho.test.tsx +11 -0
- package/src/teste/CalendarCompacto.test.tsx +37 -0
- package/src/teste/CoachmarkSemVeu.test.tsx +101 -0
- package/src/teste/CoachmarkUmPasso.test.tsx +45 -0
- package/src/teste/ColorPickerXs.test.tsx +22 -0
- package/src/teste/DataTableArvoreEVariantes.test.tsx +240 -0
- package/src/teste/DataTableCompactaETom.test.tsx +85 -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/InputGhost.test.tsx +42 -0
- package/src/teste/ListItemTooltipStandalone.test.tsx +64 -0
- package/src/teste/ModalProporcoes1Doc.test.tsx +36 -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/SegmentedControlSuperficie.test.tsx +52 -0
- package/src/teste/SelectAbertoControladoEFlutuante.test.tsx +180 -0
- package/src/teste/SelectFiltroCorETamanho.test.tsx +90 -0
- package/src/teste/SelectItemComEscopo.test.tsx +69 -0
- package/src/teste/SelectListaFiltro.test.tsx +120 -0
- package/src/teste/StatCardSecundariaETom.test.tsx +51 -0
- package/src/teste/StatCardTamanhoSm.test.tsx +42 -0
- package/src/teste/TabsCompacta.test.tsx +45 -0
- package/src/teste/TagPositivoTingido.test.tsx +60 -0
- package/src/teste/ToastAcaoEDetalhes.test.tsx +109 -0
- package/src/teste/lerArquivo.ts +13 -0
- package/src/utils/html-seguro.ts +105 -2
- package/src/utils/toast.ts +12 -2
|
@@ -14,8 +14,11 @@ export interface AccordionItemData {
|
|
|
14
14
|
/**
|
|
15
15
|
* Accordion component — lista de painéis expansíveis (sanfona).
|
|
16
16
|
* @property {AccordionItemData[]} items - Itens do accordion (id, title, content, disabled?, defaultOpen?). Conteúdo livre por item: no React, `content` aceita nó; no Vue, o slot `#content-<id>` (recebe `{ item }`).
|
|
17
|
+
* @property {string[]} open - Ids dos itens abertos (modo controlado). Sem a prop, o Accordion guarda o próprio estado (começando pelos `defaultOpen`). Com ela, quem manda é o app: abrir ou fechar chama `onOpenChange` e o painel só muda quando `open` muda. Serve para lembrar a escolha entre visitas.
|
|
18
|
+
* @property {function} onOpenChange - Chamado ao abrir ou fechar um item, com a nova lista de ids abertos (`(openIds: string[]) => void`). Funciona também sem `open`.
|
|
17
19
|
* @property {boolean} multiple - Permite manter vários painéis abertos ao mesmo tempo
|
|
18
20
|
* @property {string} variant - Estilo visual: 'default' | 'bordered' | 'separated'
|
|
21
|
+
* @property {string} titleVariant - Título dos itens: 'default' | 'neutral'. O `neutral` é o título de texto, 14px na cor do texto, sem cor de ação (como um `Button text` neutro), para o accordion do CrisDoc.
|
|
19
22
|
* @property {string} className - Classes CSS adicionais
|
|
20
23
|
* @quandoUsar Conteúdo que fica RECOLHIDO até o usuário querer: FAQ, seções opcionais de formulário, detalhes secundários. Vários podem abrir juntos com `multiple`.
|
|
21
24
|
* @quandoNaoUsar Para alternar entre visões equivalentes sempre disponíveis — use `Tabs`. Se o conteúdo é essencial, não esconda atrás de acordeão.
|
|
@@ -23,7 +26,13 @@ export interface AccordionItemData {
|
|
|
23
26
|
export interface AccordionProps {
|
|
24
27
|
items: AccordionItemData[];
|
|
25
28
|
multiple?: boolean;
|
|
29
|
+
/** Ids dos itens abertos (controlado). Sem a prop, o estado é interno. */
|
|
30
|
+
open?: string[];
|
|
31
|
+
/** Abriu ou fechou um item: recebe a nova lista de ids abertos. */
|
|
32
|
+
onOpenChange?: (openIds: string[]) => void;
|
|
26
33
|
variant?: "default" | "bordered" | "separated";
|
|
34
|
+
/** Título dos itens. `neutral`: 14px na cor do texto, peso médio. @default 'default' */
|
|
35
|
+
titleVariant?: "default" | "neutral";
|
|
27
36
|
className?: string;
|
|
28
37
|
}
|
|
29
38
|
declare function Accordion(props: AccordionProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -9,8 +9,13 @@ function Accordion(props) {
|
|
|
9
9
|
function itemList() {
|
|
10
10
|
return props.items || [];
|
|
11
11
|
}
|
|
12
|
+
function currentOpen() {
|
|
13
|
+
return props.open !== undefined && props.open !== null
|
|
14
|
+
? props.open
|
|
15
|
+
: openIds;
|
|
16
|
+
}
|
|
12
17
|
function isOpen(id) {
|
|
13
|
-
return
|
|
18
|
+
return currentOpen().includes(id);
|
|
14
19
|
}
|
|
15
20
|
function triggerId(id) {
|
|
16
21
|
return `umds-accordion-trigger-${id}`;
|
|
@@ -21,20 +26,28 @@ function Accordion(props) {
|
|
|
21
26
|
function toggle(id, disabled) {
|
|
22
27
|
if (disabled)
|
|
23
28
|
return;
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
const atuais = currentOpen();
|
|
30
|
+
let proximos;
|
|
31
|
+
if (atuais.includes(id)) {
|
|
32
|
+
proximos = atuais.filter((x) => x !== id);
|
|
26
33
|
}
|
|
27
34
|
else if (props.multiple) {
|
|
28
|
-
|
|
35
|
+
proximos = [...atuais, id];
|
|
29
36
|
}
|
|
30
37
|
else {
|
|
31
|
-
|
|
38
|
+
proximos = [id];
|
|
32
39
|
}
|
|
40
|
+
// Controlado: quem muda é o app (o `open` novo). Sem `open`, o estado interno.
|
|
41
|
+
if (props.open === undefined || props.open === null)
|
|
42
|
+
setOpenIds(proximos);
|
|
43
|
+
if (props.onOpenChange)
|
|
44
|
+
props.onOpenChange(proximos);
|
|
33
45
|
}
|
|
34
46
|
function wrapperClasses() {
|
|
35
47
|
return [
|
|
36
48
|
"umds-accordion",
|
|
37
49
|
`umds-accordion--${props.variant || "default"}`,
|
|
50
|
+
props.titleVariant === "neutral" ? "umds-accordion--title-neutral" : "",
|
|
38
51
|
props.className,
|
|
39
52
|
]
|
|
40
53
|
.filter(Boolean)
|
|
@@ -21,14 +21,23 @@
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/* O contexto cede espaço e rola sozinho: uma trilha longa não pode empurrar
|
|
24
|
-
a busca e as ações para fora da tela.
|
|
24
|
+
a busca e as ações para fora da tela.
|
|
25
|
+
Só rola na HORIZONTAL, e só quando a trilha estoura. `overflow-x: auto` sozinho
|
|
26
|
+
faz o `overflow-y` computar para `auto` também, e o `line-height` do item do
|
|
27
|
+
Breadcrumb (12px, numa linha de 12px) deixa o conteúdo 2px acima da caixa de 12px
|
|
28
|
+
(medido: scrollHeight 14, clientHeight 12): com a trilha só na página atual (sem
|
|
29
|
+
link para focar) o axe acusa `scrollable-region-focusable`. Por isso a região
|
|
30
|
+
ocupa a altura da barra (`align-self: stretch`, o conteúdo segue centralizado) e o
|
|
31
|
+
eixo vertical é cortado: não há o que rolar na vertical. */
|
|
25
32
|
.umds-app-subheader__contexto {
|
|
26
33
|
flex: 1;
|
|
34
|
+
align-self: stretch;
|
|
27
35
|
min-width: 0;
|
|
28
36
|
display: flex;
|
|
29
37
|
align-items: center;
|
|
30
38
|
gap: var(--umds-primitive-spacing-nano);
|
|
31
39
|
overflow-x: auto;
|
|
40
|
+
overflow-y: hidden;
|
|
32
41
|
scrollbar-width: none;
|
|
33
42
|
}
|
|
34
43
|
|
|
@@ -183,18 +183,10 @@
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
.umds-attachment-card__campo {
|
|
186
|
-
display: flex;
|
|
187
|
-
align-items: center;
|
|
188
|
-
gap: var(--umds-primitive-spacing-nano);
|
|
189
186
|
padding: var(--umds-primitive-spacing-nano) 0;
|
|
190
187
|
border-top: 1px solid var(--umds-semantic-color-neutral-light-medium);
|
|
191
188
|
}
|
|
192
189
|
|
|
193
|
-
.umds-attachment-card__campo-texto {
|
|
194
|
-
flex: 1;
|
|
195
|
-
min-width: 0;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
190
|
.umds-attachment-card__campo-rotulo {
|
|
199
191
|
color: var(--umds-semantic-color-neutral-dark-down);
|
|
200
192
|
font-size: var(--umds-primitive-font-size-xxxs);
|
|
@@ -202,7 +194,12 @@
|
|
|
202
194
|
text-transform: uppercase;
|
|
203
195
|
}
|
|
204
196
|
|
|
197
|
+
/* O valor e o botão de copiar na mesma linha: o botão mora dentro do <dd> (o <dl> só aceita
|
|
198
|
+
<dt> e <dd> dentro do <div>). */
|
|
205
199
|
.umds-attachment-card__campo-valor {
|
|
200
|
+
display: flex;
|
|
201
|
+
align-items: center;
|
|
202
|
+
gap: var(--umds-primitive-spacing-nano);
|
|
206
203
|
margin: var(--umds-primitive-spacing-micro) 0 0; /* 2px: respiro rótulo→valor */
|
|
207
204
|
font-size: var(--umds-primitive-font-size-xxs);
|
|
208
205
|
font-weight: var(--umds-primitive-font-weight-bold);
|
|
@@ -210,6 +207,11 @@
|
|
|
210
207
|
overflow-wrap: anywhere;
|
|
211
208
|
}
|
|
212
209
|
|
|
210
|
+
.umds-attachment-card__campo-valor-texto {
|
|
211
|
+
flex: 1;
|
|
212
|
+
min-width: 0;
|
|
213
|
+
}
|
|
214
|
+
|
|
213
215
|
/* Alvo de toque WCAG 2.5.8: 32×32. */
|
|
214
216
|
.umds-attachment-card__copiar {
|
|
215
217
|
display: inline-flex;
|
|
@@ -15,6 +15,7 @@ import './AttachmentCard.css';
|
|
|
15
15
|
* @property {boolean} closePanelOnAction - Fecha o painel e devolve o foco ao selo quando alguém aciona um botão ou link de dentro dele (o botão "Aplicar sugestão" do painel de IA). O botão de copiar um campo não conta.
|
|
16
16
|
* @property {Function} onCopyField - Avisa quando um campo do painel é copiado (recebe o campo)
|
|
17
17
|
* @property {IconProp} icon - Ícone central. Padrão: documento.
|
|
18
|
+
* @property {string} size - Tamanho do cartão: 'md' (padrão) | 'sm'. O `sm` é o compacto do CrisDoc: ícone do preview de 40px (no `md`, 48px) e ícone do rodapé de 12px (no `md`, 16px). A largura e os selos não mudam.
|
|
18
19
|
* @property {Function} onOpen - Abre o anexo. Com ele o cartão inteiro vira alvo de clique, com um único ponto de foco.
|
|
19
20
|
* @property {string} openAriaLabel - Nome acessível da ação de abrir. Padrão: "Abrir <tipo>: <arquivo>".
|
|
20
21
|
* @property {string} className - Classes CSS adicionais
|
|
@@ -60,6 +61,8 @@ export interface AttachmentCardProps {
|
|
|
60
61
|
closePanelOnAction?: boolean;
|
|
61
62
|
onCopyField?: (field: AttachmentCardField) => void;
|
|
62
63
|
icon?: IconProp;
|
|
64
|
+
/** Tamanho do cartão. `sm`: ícone do preview de 40px e ícone do rodapé de 12px. @default 'md' */
|
|
65
|
+
size?: "md" | "sm";
|
|
63
66
|
onOpen?: (event?: any) => void;
|
|
64
67
|
openAriaLabel?: string;
|
|
65
68
|
/** Complemento lido depois do selo, para dizer de qual documento ele é. Recebe o tipo de documento (ou o nome do arquivo). Troque para traduzir. @default (subject) => 'conferência de ' + subject */
|
|
@@ -29,6 +29,7 @@ function AttachmentCard(props) {
|
|
|
29
29
|
return [
|
|
30
30
|
"umds-attachment-card",
|
|
31
31
|
props.onOpen ? "umds-attachment-card--interativo" : "",
|
|
32
|
+
props.size === "sm" ? "umds-attachment-card--sm" : "",
|
|
32
33
|
props.className,
|
|
33
34
|
]
|
|
34
35
|
.filter(Boolean)
|
|
@@ -145,6 +146,12 @@ function AttachmentCard(props) {
|
|
|
145
146
|
return faFileExcel;
|
|
146
147
|
return faFile;
|
|
147
148
|
}
|
|
149
|
+
function tamanhoIconeCentral() {
|
|
150
|
+
return props.size === "sm" ? "40px" : "48px";
|
|
151
|
+
}
|
|
152
|
+
function tamanhoIconeRodape() {
|
|
153
|
+
return props.size === "sm" ? "12px" : undefined;
|
|
154
|
+
}
|
|
148
155
|
function iconeCentral() {
|
|
149
156
|
return props.icon || faFileLines;
|
|
150
157
|
}
|
|
@@ -386,6 +393,6 @@ function AttachmentCard(props) {
|
|
|
386
393
|
desligarObservadores();
|
|
387
394
|
};
|
|
388
395
|
}, []);
|
|
389
|
-
return (_jsxs("div", { className: classes(), id: props.id, children: [_jsxs("div", { className: "umds-attachment-card__topo", children: [temSelo() && !temDetalhe() ? (_jsxs("span", { className: statusClasses(), children: [seloIcone() ? (_jsx(Icon, { size: "sm", variant: "inherit", icon: seloIcone() })) : null, _jsx("span", { className: "umds-attachment-card__status-texto", children: props.statusLabel })] })) : null, temDetalhe() ? (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", "aria-haspopup": "dialog", ref: seloRef, className: statusBotaoClasses(), "aria-expanded": ariaExpandidoTexto(), "aria-controls": ariaControlsTexto(), onMouseEnter: (event) => abrir(false), onMouseLeave: (event) => agendarFechar(), onClick: (event) => alternar(), onKeyDown: (event) => teclaNoSelo(event), onBlur: (event) => focoSaiu(event), children: [seloIcone() ? (_jsx(Icon, { size: "sm", variant: "inherit", icon: seloIcone() })) : null, _jsx("span", { className: "umds-attachment-card__status-texto", children: props.statusLabel }), _jsx("span", { className: "umds-attachment-card__sr", children: seloComplemento() })] }), _jsx(Portal, { children: aberto ? (_jsx("span", { className: "umds-attachment-card__flutuante", ref: flutuanteRef, style: flutuanteEstilo(), children: _jsxs("div", { className: "umds-attachment-card__detalhe", role: "dialog", "aria-modal": "false", ref: painelRef, id: painelId(), "aria-labelledby": painelNomeadoPor(), "aria-label": painelRotulo(), tabIndex: -1, style: detalheEstilo(), onClick: (event) => cliqueNoPainel(event), onMouseEnter: (event) => cancelarFechar(), onMouseLeave: (event) => agendarFechar(), onKeyDown: (event) => teclaNoPainel(event), children: [_jsx("span", { className: "umds-attachment-card__sr", role: "status", "aria-live": "polite", children: anuncio }), props.panel ? (_jsx(_Fragment, { children: props.panel })) : (_jsx(_Fragment, { children: _jsxs(_Fragment, { children: [_jsxs("div", { className: "umds-attachment-card__detalhe-topo", children: [_jsx("span", { "aria-hidden": "true", className: detalheIconeClasses(), children: _jsx(Icon, { size: "md", variant: "inherit", icon: detalheIcone() }) }), _jsx("span", { className: "umds-attachment-card__detalhe-titulo", id: tituloId(), children: detalheTitulo() })] }), detalheDescricao() ? (_jsx("p", { className: "umds-attachment-card__detalhe-descricao", children: detalheDescricao() })) : null, detalheCampos().length > 0 ? (_jsx("dl", { className: "umds-attachment-card__campos", children: detalheCampos()?.map((campo) => (_jsxs("div", { className: "umds-attachment-card__campo", children: [
|
|
396
|
+
return (_jsxs("div", { className: classes(), id: props.id, children: [_jsxs("div", { className: "umds-attachment-card__topo", children: [temSelo() && !temDetalhe() ? (_jsxs("span", { className: statusClasses(), children: [seloIcone() ? (_jsx(Icon, { size: "sm", variant: "inherit", icon: seloIcone() })) : null, _jsx("span", { className: "umds-attachment-card__status-texto", children: props.statusLabel })] })) : null, temDetalhe() ? (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", "aria-haspopup": "dialog", ref: seloRef, className: statusBotaoClasses(), "aria-expanded": ariaExpandidoTexto(), "aria-controls": ariaControlsTexto(), onMouseEnter: (event) => abrir(false), onMouseLeave: (event) => agendarFechar(), onClick: (event) => alternar(), onKeyDown: (event) => teclaNoSelo(event), onBlur: (event) => focoSaiu(event), children: [seloIcone() ? (_jsx(Icon, { size: "sm", variant: "inherit", icon: seloIcone() })) : null, _jsx("span", { className: "umds-attachment-card__status-texto", children: props.statusLabel }), _jsx("span", { className: "umds-attachment-card__sr", children: seloComplemento() })] }), _jsx(Portal, { children: aberto ? (_jsx("span", { className: "umds-attachment-card__flutuante", ref: flutuanteRef, style: flutuanteEstilo(), children: _jsxs("div", { className: "umds-attachment-card__detalhe", role: "dialog", "aria-modal": "false", ref: painelRef, id: painelId(), "aria-labelledby": painelNomeadoPor(), "aria-label": painelRotulo(), tabIndex: -1, style: detalheEstilo(), onClick: (event) => cliqueNoPainel(event), onMouseEnter: (event) => cancelarFechar(), onMouseLeave: (event) => agendarFechar(), onKeyDown: (event) => teclaNoPainel(event), children: [_jsx("span", { className: "umds-attachment-card__sr", role: "status", "aria-live": "polite", children: anuncio }), props.panel ? (_jsx(_Fragment, { children: props.panel })) : (_jsx(_Fragment, { children: _jsxs(_Fragment, { children: [_jsxs("div", { className: "umds-attachment-card__detalhe-topo", children: [_jsx("span", { "aria-hidden": "true", className: detalheIconeClasses(), children: _jsx(Icon, { size: "md", variant: "inherit", icon: detalheIcone() }) }), _jsx("span", { className: "umds-attachment-card__detalhe-titulo", id: tituloId(), children: detalheTitulo() })] }), detalheDescricao() ? (_jsx("p", { className: "umds-attachment-card__detalhe-descricao", children: detalheDescricao() })) : null, detalheCampos().length > 0 ? (_jsx("dl", { className: "umds-attachment-card__campos", children: detalheCampos()?.map((campo) => (_jsxs("div", { className: "umds-attachment-card__campo", children: [_jsx("dt", { className: "umds-attachment-card__campo-rotulo", children: campo.label }), _jsxs("dd", { className: "umds-attachment-card__campo-valor", children: [_jsx("span", { className: "umds-attachment-card__campo-valor-texto", children: campo.value }), campo.copyable ? (_jsx("button", { type: "button", className: "umds-attachment-card__copiar", "aria-label": rotuloCopiar(campo), onClick: (event) => copiarCampo(campo), children: _jsx(Icon, { size: "sm", variant: "inherit", icon: iconeCopiar(campo) }) })) : null] })] }, campo.label))) })) : null] }) }))] }) })) : null })] })) : null] }), _jsx("div", { className: "umds-attachment-card__preview", "aria-hidden": "true", children: _jsx(Icon, { variant: "inherit", icon: iconeCentral(), customSize: tamanhoIconeCentral() }) }), _jsx("div", { className: "umds-attachment-card__tipo-area", children: props.documentType ? (_jsxs("span", { className: "umds-attachment-card__tipo", children: [_jsx("span", { className: "umds-attachment-card__tipo-texto", children: props.documentType }), props.required ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "umds-attachment-card__obrigatorio", "aria-hidden": "true", children: "*" }), _jsx("span", { className: "umds-attachment-card__sr", children: props.requiredLabel || "(obrigatório)" })] })) : null] })) : null }), _jsxs("div", { className: "umds-attachment-card__rodape", title: props.fileName, children: [_jsx("span", { className: "umds-attachment-card__rodape-icone", "aria-hidden": "true", children: _jsx(Icon, { size: "sm", variant: "inherit", icon: iconeArquivo(), customSize: tamanhoIconeRodape() }) }), props.onOpen ? (_jsx("button", { type: "button", className: "umds-attachment-card__nome umds-attachment-card__abrir", "aria-label": rotuloAbrir(), onClick: (event) => handleOpen(event), children: props.fileName })) : (_jsx("span", { className: "umds-attachment-card__nome", children: props.fileName }))] })] }));
|
|
390
397
|
}
|
|
391
398
|
export default AttachmentCard;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Variantes: neutral, positive, negative, info, primary, new
|
|
4
4
|
Aparências (appearance): solid, soft, outlined
|
|
5
5
|
Tamanhos: 2xs, xs, sm, md, lg, pill
|
|
6
|
-
Modificador: attached (rótulo colado no topo de um card)
|
|
6
|
+
Modificador: attached (rótulo colado no topo de um card), attachedSide left | right
|
|
7
7
|
============================================ */
|
|
8
8
|
|
|
9
9
|
.umds-badge {
|
|
@@ -60,12 +60,12 @@
|
|
|
60
60
|
|
|
61
61
|
/* ── Tamanhos — padding + gap por tamanho (sem height fixa) ── */
|
|
62
62
|
|
|
63
|
-
/* 2xs: 16px de altura (10 de fonte + 2 + 2 de padding + 2 de borda)
|
|
64
|
-
10px não está na escala do DS (menor
|
|
65
|
-
|
|
63
|
+
/* 2xs: 16px de altura (10 de fonte + 2 + 2 de padding + 2 de borda) e 8px de padding
|
|
64
|
+
lateral, como o selo do CrisDoc. A fonte de 10px não está na escala do DS (menor
|
|
65
|
+
degrau: 12px), sem token equivalente. */
|
|
66
66
|
.umds-badge--2xs {
|
|
67
67
|
font-size: 0.625rem; /* 10px: fora da escala, selo compacto */
|
|
68
|
-
padding: var(--umds-primitive-spacing-micro) var(--umds-primitive-spacing-
|
|
68
|
+
padding: var(--umds-primitive-spacing-micro) var(--umds-primitive-spacing-nano);
|
|
69
69
|
gap: var(--umds-primitive-spacing-micro);
|
|
70
70
|
border-radius: var(--umds-primitive-border-radius-xs); /* 4px */
|
|
71
71
|
}
|
|
@@ -252,5 +252,27 @@
|
|
|
252
252
|
position: absolute;
|
|
253
253
|
top: 0;
|
|
254
254
|
left: 0;
|
|
255
|
+
/* 22px: o rótulo do CrisDoc (24 - 2). Os tamanhos menores (xs, 2xs) sobem a ele; o md já mede 22. */
|
|
256
|
+
min-height: calc(var(--umds-primitive-spacing-xxs) - var(--umds-primitive-spacing-micro));
|
|
255
257
|
border-radius: 0 0 var(--umds-primitive-border-radius-sm) var(--umds-primitive-border-radius-sm);
|
|
256
258
|
}
|
|
259
|
+
|
|
260
|
+
/* Canto direito (attachedSide="right"): o rótulo cola no canto superior direito. */
|
|
261
|
+
.umds-badge--attached.umds-badge--attached-right {
|
|
262
|
+
left: auto;
|
|
263
|
+
right: 0;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* Neutro colado: cinza claro com texto escuro (o "Tarefa manual" do CrisDoc).
|
|
267
|
+
O `solid` neutro é cinza escuro com texto branco, pesado demais para um rótulo de card. */
|
|
268
|
+
.umds-badge--attached.umds-badge--neutral.umds-badge--solid {
|
|
269
|
+
background-color: var(--umds-semantic-color-neutral-light-medium);
|
|
270
|
+
color: var(--umds-semantic-color-neutral-dark-pure);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* attachedTone="dark": o neutro escuro do CrisDoc. O ardósia do CrisDoc dá 2,9:1 com texto branco; o par
|
|
274
|
+
neutral-dark-down + branco dá 7,3:1 e inverte sozinho nos temas escuros. */
|
|
275
|
+
.umds-badge--attached.umds-badge--neutral.umds-badge--solid.umds-badge--attached-dark {
|
|
276
|
+
background-color: var(--umds-semantic-color-neutral-dark-down);
|
|
277
|
+
color: var(--umds-semantic-color-neutral-light-pure);
|
|
278
|
+
}
|
|
@@ -13,19 +13,35 @@ export interface BadgeProps {
|
|
|
13
13
|
*/
|
|
14
14
|
variant?: "neutral" | "positive" | "negative" | "info" | "primary" | "new";
|
|
15
15
|
/**
|
|
16
|
-
* Tamanho do badge. `2xs` é o compacto (16px, fonte de 10px
|
|
17
|
-
* setor e de contagem dentro de linha de lista e card.
|
|
16
|
+
* Tamanho do badge. `2xs` é o compacto (16px, fonte de 10px, 8px de padding
|
|
17
|
+
* lateral) para selo de setor e de contagem dentro de linha de lista e card.
|
|
18
18
|
* @default 'md'
|
|
19
19
|
*/
|
|
20
20
|
size?: "2xs" | "xs" | "sm" | "md" | "lg" | "pill";
|
|
21
21
|
/**
|
|
22
|
-
* Cola o badge no canto superior
|
|
22
|
+
* Cola o badge no canto superior do card (rótulo de card, como
|
|
23
23
|
* "Tarefa pendente"): cantos de baixo arredondados, os de cima retos.
|
|
24
24
|
* Posiciona em `absolute`, então o pai precisa de `position: relative`
|
|
25
|
-
* (o `Card` e o `Box` com `position` já servem).
|
|
25
|
+
* (o `Card` e o `Box` com `position` já servem). O canto é o esquerdo, ou o
|
|
26
|
+
* direito com `attachedSide="right"`. Com `variant="neutral"` e `appearance="solid"` o
|
|
27
|
+
* rótulo colado é o cinza claro com texto escuro (o "Tarefa manual" do CrisDoc), e não o
|
|
28
|
+
* cinza escuro do `solid`, pesado demais para um rótulo de card.
|
|
26
29
|
* @default false
|
|
27
30
|
*/
|
|
28
31
|
attached?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Em que canto do card o `attached` cola: `left` (padrão) ou `right`.
|
|
34
|
+
* Só vale com `attached`.
|
|
35
|
+
* @default 'left'
|
|
36
|
+
*/
|
|
37
|
+
attachedSide?: "left" | "right";
|
|
38
|
+
/**
|
|
39
|
+
* Tom do rótulo colado neutro e sólido: `light` (padrão, cinza claro com texto escuro) ou `dark` (cinza
|
|
40
|
+
* escuro com texto branco, o rótulo escuro do CrisDoc). Só vale com `attached`, `variant="neutral"` e
|
|
41
|
+
* `appearance="solid"`; as outras variantes ignoram. O rótulo colado mede 22px de altura.
|
|
42
|
+
* @default 'light'
|
|
43
|
+
*/
|
|
44
|
+
attachedTone?: "light" | "dark";
|
|
29
45
|
/**
|
|
30
46
|
* Estilo visual do badge.
|
|
31
47
|
* @default 'soft'
|
|
@@ -8,6 +8,12 @@ function Badge(props) {
|
|
|
8
8
|
`umds-badge--${props.size || "md"}`,
|
|
9
9
|
`umds-badge--${props.appearance || "soft"}`,
|
|
10
10
|
props.attached ? "umds-badge--attached" : "",
|
|
11
|
+
props.attached && props.attachedSide === "right"
|
|
12
|
+
? "umds-badge--attached-right"
|
|
13
|
+
: "",
|
|
14
|
+
props.attached && props.attachedTone === "dark"
|
|
15
|
+
? "umds-badge--attached-dark"
|
|
16
|
+
: "",
|
|
11
17
|
props.className,
|
|
12
18
|
]
|
|
13
19
|
.filter(Boolean)
|
|
@@ -56,6 +56,11 @@
|
|
|
56
56
|
font-size: var(--umds-primitive-font-size-xxxs); /* 12px */
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/* density="comfortable": o sm com o padding de 12px do CrisDoc. O botão só de ícone tem largura fixa e fica de fora. */
|
|
60
|
+
.umds-button--sm.umds-button--comfortable:not(.umds-button--icon) {
|
|
61
|
+
padding: 0 var(--umds-primitive-spacing-mini); /* 12px */
|
|
62
|
+
}
|
|
63
|
+
|
|
59
64
|
.umds-button--md {
|
|
60
65
|
height: var(--umds-primitive-spacing-sm); /* 40px */
|
|
61
66
|
padding: 0 var(--umds-primitive-spacing-xxxs); /* 16px */
|
|
@@ -3,6 +3,7 @@ import './Button.css';
|
|
|
3
3
|
* Button — botão de ação com variantes, tamanhos, ícones e estado de carregamento.
|
|
4
4
|
* @property {string} variant - Variante visual: 'primary' | 'secondary' | 'neutral' | 'neutral-text' | 'text' | 'negative' | 'negative-outlined' | 'icon'. `neutral` é o contornado cinza; `neutral-text` é o texto sem borda e sem azul, para barras de ações onde nenhum botão é a ação principal.
|
|
5
5
|
* @property {string} size - Tamanho do botão: 'xs' (26px, para dentro de card e linha de lista) | 'sm' (32px) | 'md' (40px) | 'lg' (48px)
|
|
6
|
+
* @property {string} density - 'default' (padrão) | 'comfortable'. O `comfortable` leva o padding lateral do `sm` de 8px a 12px (o botão pequeno do CrisDoc); a altura (32px) não muda. Nos outros tamanhos não faz diferença: `xs` já usa 12px, `md` 16px e `lg` 24px. Sem a prop, nenhum botão existente muda.
|
|
6
7
|
* @property {boolean} disabled - Desabilita o botão
|
|
7
8
|
* @property {string} type - Tipo nativo do botão: 'button' | 'submit' | 'reset'
|
|
8
9
|
* @property {Function} onClick - Callback disparado no clique
|
|
@@ -27,6 +28,8 @@ import './Button.css';
|
|
|
27
28
|
export interface ButtonProps {
|
|
28
29
|
variant?: "primary" | "secondary" | "neutral" | "neutral-text" | "text" | "negative" | "negative-outlined" | "icon";
|
|
29
30
|
size?: "xs" | "sm" | "md" | "lg";
|
|
31
|
+
/** `comfortable`: o `sm` com 12px de padding lateral (o padrão é 8px). @default 'default' */
|
|
32
|
+
density?: "default" | "comfortable";
|
|
30
33
|
disabled?: boolean;
|
|
31
34
|
type?: "button" | "submit" | "reset";
|
|
32
35
|
children?: any;
|
|
@@ -9,6 +9,7 @@ function Button(props) {
|
|
|
9
9
|
"umds-button",
|
|
10
10
|
`umds-button--${props.variant || "primary"}`,
|
|
11
11
|
`umds-button--${props.size || "md"}`,
|
|
12
|
+
props.density === "comfortable" ? "umds-button--comfortable" : "",
|
|
12
13
|
props.fullWidth ? "umds-button--full-width" : "",
|
|
13
14
|
props.className,
|
|
14
15
|
]
|
|
@@ -171,6 +171,16 @@
|
|
|
171
171
|
cursor: pointer;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
/* density="compact": evento de 20px (16 + 4: o degrau do DS mais perto dos 20,5 do CrisDoc) e 4px de padding
|
|
175
|
+
lateral. A linha tem 16px de altura e 2px de padding em cima e embaixo. O título continua cortado com
|
|
176
|
+
reticências só quando passa da largura (a regra do título, abaixo). */
|
|
177
|
+
.umds-calendar--compact .umds-calendar__event {
|
|
178
|
+
min-height: calc(var(--umds-primitive-spacing-xxxs) + var(--umds-primitive-spacing-quarck)); /* 20px */
|
|
179
|
+
padding: var(--umds-primitive-spacing-micro) var(--umds-primitive-spacing-quarck); /* 2px 4px */
|
|
180
|
+
line-height: var(--umds-primitive-spacing-xxxs); /* 16px */
|
|
181
|
+
align-items: center;
|
|
182
|
+
}
|
|
183
|
+
|
|
174
184
|
.umds-calendar__event:hover {
|
|
175
185
|
filter: brightness(0.96);
|
|
176
186
|
}
|
|
@@ -52,8 +52,9 @@ export interface CalendarDayCell {
|
|
|
52
52
|
*
|
|
53
53
|
* A data mostrada (`date`) e a visão (`view`) são controláveis (`onDateChange`,
|
|
54
54
|
* `onViewChange`) ou internas (`defaultDate`, `defaultView`). O cabeçalho aceita
|
|
55
|
-
*
|
|
56
|
-
*
|
|
55
|
+
* um filtro de escopo à esquerda e a legenda à direita: no React, as props `headerStart`
|
|
56
|
+
* e `headerEnd` (um nó); no Vue, os slots `#header-start` e `#header-end` (com hífen: o
|
|
57
|
+
* Vue lê o nome do slot como está, e `#headerStart` não chega). Nomes de mês e de dia saem do `Intl` no `locale` pedido;
|
|
57
58
|
* os demais textos são props.
|
|
58
59
|
* @property {CalendarEvent[]} events - Eventos (lista plana)
|
|
59
60
|
* @property {string} view - Visão: 'month' | 'week' (controlada)
|
|
@@ -64,6 +65,7 @@ export interface CalendarDayCell {
|
|
|
64
65
|
* @property {string} today - O "hoje" do calendário (`YYYY-MM-DD`; padrão a data do sistema). Útil em testes e em demonstração.
|
|
65
66
|
* @property {number} weekStartsOn - Primeiro dia da semana: 0 domingo (padrão) | 1 segunda
|
|
66
67
|
* @property {number} maxVisible - Eventos por dia na visão de mês antes do "+N" (padrão 3)
|
|
68
|
+
* @property {string} density - 'default' | 'compact'. O `compact` aperta o evento: altura de 20px (o degrau do DS mais perto dos 20,5 do CrisDoc; o padrão dá 25) e 4px de padding lateral (o padrão tem 8), para o título caber inteiro onde antes cortava com reticências. O título só é cortado (com reticências) quando de fato passa da largura da célula.
|
|
67
69
|
* @property {string} locale - Idioma dos nomes de mês e de dia (padrão 'pt-BR')
|
|
68
70
|
* @property {string} ariaLabel - Nome acessível da grade (padrão: o título, ex.: "Maio 2026")
|
|
69
71
|
* @property {string} previousLabel - Nome do botão de voltar (padrão "Anterior")
|
|
@@ -82,8 +84,8 @@ export interface CalendarDayCell {
|
|
|
82
84
|
* @property {function} onDateChange - Mudou a data de referência (navegação): recebe `YYYY-MM-DD`
|
|
83
85
|
* @property {function} onViewChange - Mudou a visão: recebe 'month' | 'week'
|
|
84
86
|
* @property {string} className - Classes CSS adicionais
|
|
85
|
-
* @slot
|
|
86
|
-
* @slot
|
|
87
|
+
* @slot header-start - Só no Vue (`#header-start`, com hífen). Cabeçalho, à esquerda: um filtro de escopo. No React, a prop `headerStart`.
|
|
88
|
+
* @slot header-end - Só no Vue (`#header-end`, com hífen). Cabeçalho, antes do seletor de visão: a legenda. No React, a prop `headerEnd`.
|
|
87
89
|
* @quandoUsar Mostrar prazos, vencimentos e compromissos distribuídos no tempo: agenda de documentos, de tarefas, de assinaturas.
|
|
88
90
|
* @quandoNaoUsar Para escolher UMA data num campo, use `DatePicker`. Para uma sequência de acontecimentos sem grade de dias, use `Timeline`. Para uma grade de horas (agenda com horário de início e fim), este componente não serve: não tem eixo de horas.
|
|
89
91
|
*/
|
|
@@ -98,6 +100,8 @@ export interface CalendarProps {
|
|
|
98
100
|
today?: string;
|
|
99
101
|
weekStartsOn?: 0 | 1;
|
|
100
102
|
maxVisible?: number;
|
|
103
|
+
/** `compact`: evento de 20px com 4px de padding lateral. @default 'default' */
|
|
104
|
+
density?: "default" | "compact";
|
|
101
105
|
locale?: string;
|
|
102
106
|
ariaLabel?: string;
|
|
103
107
|
previousLabel?: string;
|
|
@@ -111,9 +115,9 @@ export interface CalendarProps {
|
|
|
111
115
|
moreAriaLabel?: string;
|
|
112
116
|
dayLabel?: (iso: string, count: number) => string;
|
|
113
117
|
className?: string;
|
|
114
|
-
/**
|
|
118
|
+
/** Cabeçalho, à esquerda: um filtro de escopo. React: o nó nesta prop. Vue: o slot `#header-start` (com hífen). */
|
|
115
119
|
headerStart?: any;
|
|
116
|
-
/**
|
|
120
|
+
/** Cabeçalho, à direita, antes do seletor de visão: a legenda. React: o nó nesta prop. Vue: o slot `#header-end` (com hífen). */
|
|
117
121
|
headerEnd?: any;
|
|
118
122
|
onDayClick?: (iso: string) => void;
|
|
119
123
|
onEventClick?: (event: CalendarEvent) => void;
|
|
@@ -100,7 +100,12 @@ function Calendar(props) {
|
|
|
100
100
|
];
|
|
101
101
|
}
|
|
102
102
|
function rootClasses() {
|
|
103
|
-
return [
|
|
103
|
+
return [
|
|
104
|
+
"umds-calendar",
|
|
105
|
+
"umds-calendar--" + viewValue(),
|
|
106
|
+
props.density === "compact" ? "umds-calendar--compact" : "",
|
|
107
|
+
props.className,
|
|
108
|
+
]
|
|
104
109
|
.filter(Boolean)
|
|
105
110
|
.join(" ");
|
|
106
111
|
}
|
|
@@ -107,6 +107,16 @@
|
|
|
107
107
|
border: 0;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/* ── modal={false}: sem véu nem recorte, a página segue clicável ──
|
|
111
|
+
A raiz cobre a tela só para posicionar o cartão; quem recebe o ponteiro é o cartão. */
|
|
112
|
+
.umds-coachmark--modeless {
|
|
113
|
+
pointer-events: none;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.umds-coachmark--modeless .umds-coachmark__card {
|
|
117
|
+
pointer-events: auto;
|
|
118
|
+
}
|
|
119
|
+
|
|
110
120
|
@media (forced-colors: active) {
|
|
111
121
|
.umds-coachmark__card {
|
|
112
122
|
border: var(--umds-primitive-border-width-thin) solid CanvasText;
|
|
@@ -23,7 +23,8 @@ export interface CoachmarkStep {
|
|
|
23
23
|
* O cartão é um diálogo modal (`role="dialog"`, `aria-modal`): o foco entra nele
|
|
24
24
|
* ao abrir, o Tab fica preso nele, Esc sai do tour, as setas esquerda e direita
|
|
25
25
|
* voltam e avançam, e o foco volta ao elemento de antes ao fechar. A cada passo o
|
|
26
|
-
* leitor de tela ouve "2 de 8: título" (região `aria-live`).
|
|
26
|
+
* leitor de tela ouve "2 de 8: título" (região `aria-live`). Com um passo só não há contador ("1 de 1" não
|
|
27
|
+
* diz nada): o cartão mostra o título e o leitor de tela ouve só ele.
|
|
27
28
|
*
|
|
28
29
|
* O componente é CONTROLADO por `open`: o tour só existe com `open` verdadeiro, e
|
|
29
30
|
* `onClose(motivo)` avisa quando ele quer fechar (`"finish"` no último
|
|
@@ -38,7 +39,13 @@ export interface CoachmarkStep {
|
|
|
38
39
|
*
|
|
39
40
|
* Limite: a página atrás fica inerte (o véu captura o clique), então o alvo
|
|
40
41
|
* não é clicável durante o tour.
|
|
42
|
+
*
|
|
43
|
+
* `modal={false}` tira o véu, o recorte e o foco preso: o cartão vira um popover
|
|
44
|
+
* ancorado no alvo, para uma dica pontual (um passo, "Novo: agora dá para * arrastar"). A página segue clicável, o foco entra no cartão ao abrir mas o Tab
|
|
45
|
+
* pode sair dele, o Esc fecha de qualquer lugar da página e o cartão vira um
|
|
46
|
+
* `role="dialog"` com `aria-modal="false"`.
|
|
41
47
|
* @property {boolean} open - Mostra o tour
|
|
48
|
+
* @property {boolean} modal - `true` (padrão): véu, recorte e foco preso. `false`: sem véu nem foco preso, como um popover ancorado, para dica pontual
|
|
42
49
|
* @property {CoachmarkStep[]} steps - Os passos, em ordem
|
|
43
50
|
* @property {number} step - Passo atual, a partir de 0 (controlado)
|
|
44
51
|
* @property {number} defaultStep - Passo inicial (não controlado)
|
|
@@ -57,6 +64,12 @@ export interface CoachmarkStep {
|
|
|
57
64
|
*/
|
|
58
65
|
export interface CoachmarkProps {
|
|
59
66
|
open?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* `true` (padrão): véu, recorte e foco preso. `false`: o cartão é um popover
|
|
69
|
+
* ancorado, sem véu nem foco preso. `any` de propósito: no Vue uma prop `boolean`
|
|
70
|
+
* sem valor chega como `false`, e o tour modal viraria sem véu por engano.
|
|
71
|
+
*/
|
|
72
|
+
modal?: any;
|
|
60
73
|
steps?: CoachmarkStep[];
|
|
61
74
|
step?: number;
|
|
62
75
|
defaultStep?: number;
|
|
@@ -12,6 +12,7 @@ function Coachmark(props) {
|
|
|
12
12
|
const cleanupRef = useRef(null);
|
|
13
13
|
const previousFocusRef = useRef(null);
|
|
14
14
|
const attachedRef = useRef(null);
|
|
15
|
+
const escDocRef = useRef(null);
|
|
15
16
|
const [internalStep, setInternalStep] = useState(() => props.defaultStep || 0);
|
|
16
17
|
const [uid, setUid] = useState(() => "");
|
|
17
18
|
const [active, setActive] = useState(() => false);
|
|
@@ -61,7 +62,12 @@ function Coachmark(props) {
|
|
|
61
62
|
.replace("{current}", String(index() + 1))
|
|
62
63
|
.replace("{total}", String(stepCount()));
|
|
63
64
|
}
|
|
65
|
+
function hasCounter() {
|
|
66
|
+
return stepCount() > 1;
|
|
67
|
+
}
|
|
64
68
|
function announceText() {
|
|
69
|
+
if (!hasCounter())
|
|
70
|
+
return currentStep().title;
|
|
65
71
|
return counterText() + ": " + currentStep().title;
|
|
66
72
|
}
|
|
67
73
|
function nextText() {
|
|
@@ -75,6 +81,14 @@ function Coachmark(props) {
|
|
|
75
81
|
function skipText() {
|
|
76
82
|
return props.skipLabel || "Sair do tour";
|
|
77
83
|
}
|
|
84
|
+
function isModal() {
|
|
85
|
+
if (props.modal === undefined || props.modal === null)
|
|
86
|
+
return true;
|
|
87
|
+
return props.modal !== false && props.modal !== "false";
|
|
88
|
+
}
|
|
89
|
+
function ariaModalText() {
|
|
90
|
+
return isModal() ? "true" : "false";
|
|
91
|
+
}
|
|
78
92
|
function showSkip() {
|
|
79
93
|
return !props.hideSkip;
|
|
80
94
|
}
|
|
@@ -85,6 +99,7 @@ function Coachmark(props) {
|
|
|
85
99
|
return [
|
|
86
100
|
"umds-coachmark",
|
|
87
101
|
hasTarget ? "" : "umds-coachmark--no-target",
|
|
102
|
+
isModal() ? "" : "umds-coachmark--modeless",
|
|
88
103
|
positioned ? "umds-coachmark--ready" : "",
|
|
89
104
|
]
|
|
90
105
|
.filter(Boolean)
|
|
@@ -196,6 +211,23 @@ function Coachmark(props) {
|
|
|
196
211
|
cleanupRef.current = null;
|
|
197
212
|
}
|
|
198
213
|
}
|
|
214
|
+
function ligarEsc() {
|
|
215
|
+
if (typeof document === "undefined" || escDocRef.current)
|
|
216
|
+
return;
|
|
217
|
+
const ouvir = (event) => {
|
|
218
|
+
// O Esc dentro do cartão já fechou (e deu preventDefault): não fecha duas vezes.
|
|
219
|
+
if (event.key === "Escape" && !event.defaultPrevented)
|
|
220
|
+
fechar("escape");
|
|
221
|
+
};
|
|
222
|
+
escDocRef.current = ouvir;
|
|
223
|
+
document.addEventListener("keydown", ouvir);
|
|
224
|
+
}
|
|
225
|
+
function desligarEsc() {
|
|
226
|
+
if (typeof document === "undefined" || !escDocRef.current)
|
|
227
|
+
return;
|
|
228
|
+
document.removeEventListener("keydown", escDocRef.current);
|
|
229
|
+
escDocRef.current = null;
|
|
230
|
+
}
|
|
199
231
|
function sincronizar() {
|
|
200
232
|
if (typeof document === "undefined")
|
|
201
233
|
return;
|
|
@@ -210,12 +242,18 @@ function Coachmark(props) {
|
|
|
210
242
|
if (show() && !active) {
|
|
211
243
|
setActive(true);
|
|
212
244
|
setTentativas(0);
|
|
213
|
-
|
|
245
|
+
// Guarda só na primeira vez: no React o `active` leva um render para
|
|
246
|
+
// valer, e um segundo `sincronizar` antes disso guardaria o próprio
|
|
247
|
+
// cartão (já focado) como "o elemento de antes".
|
|
248
|
+
if (!previousFocusRef.current)
|
|
249
|
+
previousFocusRef.current = document.activeElement;
|
|
214
250
|
ligar();
|
|
215
251
|
if (cardRef.current)
|
|
216
252
|
cardRef.current.focus({
|
|
217
253
|
preventScroll: true,
|
|
218
254
|
});
|
|
255
|
+
if (!isModal())
|
|
256
|
+
ligarEsc();
|
|
219
257
|
}
|
|
220
258
|
else if (show() && active && attachedRef.current !== index()) {
|
|
221
259
|
ligar();
|
|
@@ -223,15 +261,22 @@ function Coachmark(props) {
|
|
|
223
261
|
else if (!show() && active) {
|
|
224
262
|
setActive(false);
|
|
225
263
|
desligar();
|
|
264
|
+
desligarEsc();
|
|
226
265
|
attachedRef.current = null;
|
|
227
266
|
setHasTarget(false);
|
|
228
267
|
setPositioned(false);
|
|
229
268
|
if (props.step === undefined)
|
|
230
269
|
setInternalStep(props.defaultStep || 0);
|
|
231
|
-
|
|
270
|
+
// Sem véu a pessoa pode ter ido para outro lugar da página: o foco só volta se ela não foi.
|
|
271
|
+
const focoLivre = isModal() ||
|
|
272
|
+
document.activeElement === document.body ||
|
|
273
|
+
!document.activeElement;
|
|
274
|
+
if (previousFocusRef.current &&
|
|
275
|
+
previousFocusRef.current.focus &&
|
|
276
|
+
focoLivre) {
|
|
232
277
|
previousFocusRef.current.focus();
|
|
233
|
-
previousFocusRef.current = null;
|
|
234
278
|
}
|
|
279
|
+
previousFocusRef.current = null;
|
|
235
280
|
}
|
|
236
281
|
}
|
|
237
282
|
function irPara(proximo) {
|
|
@@ -274,7 +319,7 @@ function Coachmark(props) {
|
|
|
274
319
|
handlePrevious();
|
|
275
320
|
return;
|
|
276
321
|
}
|
|
277
|
-
if (event.key !== "Tab" || !cardRef.current)
|
|
322
|
+
if (event.key !== "Tab" || !cardRef.current || !isModal())
|
|
278
323
|
return;
|
|
279
324
|
// Foco preso: Tab e Shift+Tab giram dentro do cartão.
|
|
280
325
|
const focaveis = cardRef.current.querySelectorAll("button:not([disabled])");
|
|
@@ -314,8 +359,9 @@ function Coachmark(props) {
|
|
|
314
359
|
useEffect(() => {
|
|
315
360
|
return () => {
|
|
316
361
|
desligar();
|
|
362
|
+
desligarEsc();
|
|
317
363
|
};
|
|
318
364
|
}, []);
|
|
319
|
-
return (_jsx(_Fragment, { children: show() ? (_jsx(_Fragment, { children: _jsx(Portal, { children: _jsxs("div", { className: rootClasses(), children: [_jsx("div", { "aria-hidden": "true", className: "umds-coachmark__veil", onMouseDown: (event) => handleVeilMouseDown(event) }), hasTarget ? (_jsx("div", { "aria-hidden": "true", className: "umds-coachmark__cutout", style: cutoutStyle() })) : null, _jsxs("div", { role: "dialog",
|
|
365
|
+
return (_jsx(_Fragment, { children: show() ? (_jsx(_Fragment, { children: _jsx(Portal, { children: _jsxs("div", { className: rootClasses(), children: [isModal() ? (_jsx("div", { "aria-hidden": "true", className: "umds-coachmark__veil", onMouseDown: (event) => handleVeilMouseDown(event) })) : null, hasTarget && isModal() ? (_jsx("div", { "aria-hidden": "true", className: "umds-coachmark__cutout", style: cutoutStyle() })) : null, _jsxs("div", { role: "dialog", ref: cardRef, style: cardStyle(), "aria-modal": ariaModalText(), "aria-labelledby": titleId(), "aria-describedby": contentId(), tabIndex: -1, onKeyDown: (event) => handleKeyDown(event), className: cardClasses(), children: [_jsx("span", { "aria-live": "polite", className: "umds-coachmark__sr", children: announceText() }), hasCounter() ? (_jsx(Text, { as: "span", variant: "body2", weight: "bold", color: "primary", "aria-hidden": "true", children: counterText() })) : null, _jsx(Text, { as: "h2", variant: "subtitle1", weight: "bold", id: titleId(), children: currentStep().title }), currentStep().content ? (_jsx(Text, { as: "p", variant: "body2", color: "secondary", id: contentId(), children: currentStep().content })) : null, _jsxs("div", { className: "umds-coachmark__footer", children: [showSkip() ? (_jsx(Button, { variant: "text", size: "sm", onClick: (event) => fechar("skip"), children: skipText() })) : null, _jsxs("span", { className: "umds-coachmark__actions", children: [!isFirst() ? (_jsx(Button, { variant: "secondary", size: "md", onClick: (event) => handlePrevious(), children: previousText() })) : null, _jsx(Button, { variant: "primary", size: "md", onClick: (event) => handleNext(), children: nextText() })] })] })] })] }) }) })) : null }));
|
|
320
366
|
}
|
|
321
367
|
export default Coachmark;
|