@1doc/1ds-react 2.10.0 → 2.11.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.
Files changed (92) hide show
  1. package/CHANGELOG.md +235 -0
  2. package/dist/components/Accordion/Accordion.d.ts +6 -0
  3. package/dist/components/Accordion/Accordion.js +17 -5
  4. package/dist/components/Badge/Badge.css +13 -4
  5. package/dist/components/Badge/Badge.d.ts +9 -2
  6. package/dist/components/Badge/Badge.js +3 -0
  7. package/dist/components/Button/Button.css +5 -0
  8. package/dist/components/Button/Button.d.ts +3 -0
  9. package/dist/components/Button/Button.js +1 -0
  10. package/dist/components/Calendar/Calendar.css +10 -0
  11. package/dist/components/Calendar/Calendar.d.ts +3 -0
  12. package/dist/components/Calendar/Calendar.js +6 -1
  13. package/dist/components/Coachmark/Coachmark.d.ts +2 -1
  14. package/dist/components/Coachmark/Coachmark.js +6 -1
  15. package/dist/components/CodeBlock/CodeBlock.css +3 -3
  16. package/dist/components/CodeBlock/CodeBlock.d.ts +2 -2
  17. package/dist/components/DataTable/DataTable.css +42 -0
  18. package/dist/components/DataTable/DataTable.d.ts +9 -2
  19. package/dist/components/DataTable/DataTable.js +15 -0
  20. package/dist/components/Input/Input.css +9 -0
  21. package/dist/components/Input/Input.d.ts +3 -0
  22. package/dist/components/Input/Input.js +1 -0
  23. package/dist/components/Kbd/Kbd.css +3 -3
  24. package/dist/components/Kbd/Kbd.d.ts +1 -1
  25. package/dist/components/Modal/Modal.css +19 -0
  26. package/dist/components/Modal/Modal.d.ts +7 -0
  27. package/dist/components/Modal/Modal.js +1 -0
  28. package/dist/components/RichTextEditor/RichTextEditor.d.ts +2 -2
  29. package/dist/components/RichTextEditor/RichTextEditor.js +0 -5
  30. package/dist/components/SegmentedControl/SegmentedControl.css +46 -0
  31. package/dist/components/SegmentedControl/SegmentedControl.d.ts +3 -0
  32. package/dist/components/SegmentedControl/SegmentedControl.js +1 -0
  33. package/dist/components/Select/Select.css +28 -2
  34. package/dist/components/Select/Select.d.ts +7 -0
  35. package/dist/components/Select/Select.js +55 -9
  36. package/dist/components/StatCard/StatCard.css +8 -0
  37. package/dist/components/StatCard/StatCard.d.ts +3 -0
  38. package/dist/components/StatCard/StatCard.js +1 -0
  39. package/dist/components/Tabs/Tabs.css +11 -0
  40. package/dist/components/Tabs/Tabs.d.ts +7 -0
  41. package/dist/components/Tabs/Tabs.js +7 -1
  42. package/dist/components/Tag/Tag.css +3 -1
  43. package/dist/teste/AccordionControlado.test.d.ts +1 -0
  44. package/dist/teste/AccordionControlado.test.js +73 -0
  45. package/dist/teste/BadgeAttachedTomEPadding.test.d.ts +1 -0
  46. package/dist/teste/BadgeAttachedTomEPadding.test.js +43 -0
  47. package/dist/teste/ButtonDensidade.test.d.ts +1 -0
  48. package/dist/teste/ButtonDensidade.test.js +28 -0
  49. package/dist/teste/CalendarCompacto.test.d.ts +1 -0
  50. package/dist/teste/CalendarCompacto.test.js +29 -0
  51. package/dist/teste/CoachmarkUmPasso.test.d.ts +1 -0
  52. package/dist/teste/CoachmarkUmPasso.test.js +34 -0
  53. package/dist/teste/DataTableCompactaETom.test.d.ts +1 -0
  54. package/dist/teste/DataTableCompactaETom.test.js +69 -0
  55. package/dist/teste/InputGhost.test.d.ts +1 -0
  56. package/dist/teste/InputGhost.test.js +32 -0
  57. package/dist/teste/ModalProporcoes1Doc.test.d.ts +1 -0
  58. package/dist/teste/ModalProporcoes1Doc.test.js +32 -0
  59. package/dist/teste/RichTextEditorEstiloDeTexto.test.js +7 -7
  60. package/dist/teste/SegmentedControlSuperficie.test.d.ts +1 -0
  61. package/dist/teste/SegmentedControlSuperficie.test.js +39 -0
  62. package/dist/teste/SelectItemComEscopo.test.d.ts +1 -0
  63. package/dist/teste/SelectItemComEscopo.test.js +54 -0
  64. package/dist/teste/SelectListaFiltro.test.d.ts +1 -0
  65. package/dist/teste/SelectListaFiltro.test.js +102 -0
  66. package/dist/teste/StatCardTamanhoSm.test.d.ts +1 -0
  67. package/dist/teste/StatCardTamanhoSm.test.js +32 -0
  68. package/dist/teste/TabsCompacta.test.d.ts +1 -0
  69. package/dist/teste/TabsCompacta.test.js +35 -0
  70. package/dist/teste/TagPositivoTingido.test.d.ts +1 -0
  71. package/dist/teste/TagPositivoTingido.test.js +54 -0
  72. package/dist/teste/lerArquivo.d.ts +1 -0
  73. package/dist/teste/lerArquivo.js +12 -0
  74. package/dist/utils/html-seguro.js +1 -1
  75. package/package.json +3 -3
  76. package/src/teste/AccordionControlado.test.tsx +87 -0
  77. package/src/teste/BadgeAttachedTomEPadding.test.tsx +60 -0
  78. package/src/teste/ButtonDensidade.test.tsx +40 -0
  79. package/src/teste/CalendarCompacto.test.tsx +37 -0
  80. package/src/teste/CoachmarkUmPasso.test.tsx +45 -0
  81. package/src/teste/DataTableCompactaETom.test.tsx +85 -0
  82. package/src/teste/InputGhost.test.tsx +42 -0
  83. package/src/teste/ModalProporcoes1Doc.test.tsx +36 -0
  84. package/src/teste/RichTextEditorEstiloDeTexto.test.tsx +8 -8
  85. package/src/teste/SegmentedControlSuperficie.test.tsx +52 -0
  86. package/src/teste/SelectItemComEscopo.test.tsx +69 -0
  87. package/src/teste/SelectListaFiltro.test.tsx +120 -0
  88. package/src/teste/StatCardTamanhoSm.test.tsx +42 -0
  89. package/src/teste/TabsCompacta.test.tsx +45 -0
  90. package/src/teste/TagPositivoTingido.test.tsx +60 -0
  91. package/src/teste/lerArquivo.ts +13 -0
  92. package/src/utils/html-seguro.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,240 @@
1
1
  # @1doc/1ds-react
2
2
 
3
+ ## 2.11.1
4
+
5
+ ### Patch Changes
6
+
7
+ - ffc6176: remove(tokens): `--umds-semantic-font-family-mono` e `--umds-primitive-font-family-mono` saem
8
+
9
+ REMOÇÃO de token, não acréscimo. O DS não cria token novo: a família monoespaçada (introduzida no 0.6.0) volta a não
10
+ existir. Como estamos em 0.x, a remoção sai como minor nos tokens.
11
+
12
+ - `Kbd` e `CodeBlock` passam a usar `--umds-semantic-font-family-base`: o texto da tecla e do código sai na fonte do DS,
13
+ não mais monoespaçada (muda a largura dos caracteres; o espaço e as quebras de linha do `CodeBlock` seguem mantidos).
14
+ - `RichTextEditor`: a opção "Monoespaçada" da ferramenta `fontFamily` sai (o label `fontFamilyMono` também). Fica só a
15
+ opção "Padrão" (`fontFamilyBase`). A higienização do valor passa a aceitar só `font-family-base` em `font-family`:
16
+ um `<span style="font-family: var(--umds-semantic-font-family-mono)">` salvo antes perde a declaração ao carregar.
17
+ - Quem usou o token: troque por `--umds-semantic-font-family-base`.
18
+
19
+ ## 2.11.0
20
+
21
+ ### Minor Changes
22
+
23
+ - bf2cfdc: feat(accordion): `open` e `onOpenChange`, abertura controlada
24
+
25
+ `open` (lista de ids dos itens abertos) e `onOpenChange` (recebe a nova lista a cada abrir ou fechar). Com `open`, quem manda é o
26
+ app: o clique só pede e o painel muda quando `open` muda, para lembrar a escolha entre visitas (as seções do CrisDoc). Sem `open`, o
27
+ Accordion guarda o próprio estado, como antes (começando pelos `defaultOpen`), e `onOpenChange` também avisa. `open=[]` é controlado
28
+ (tudo fechado), não ausência. Item desabilitado não pede nada.
29
+
30
+ ```vue
31
+ <Accordion
32
+ :items="secoes"
33
+ multiple
34
+ :open="abertos"
35
+ :onOpenChange="(ids) => (abertos = ids)"
36
+ />
37
+ ```
38
+
39
+ - 9c8baa3: feat(badge): `2xs` com 8px de padding lateral; `attached` com 22px e o tom escuro como opção (`attachedTone`)
40
+
41
+ - `size="2xs"`: o padding lateral passa de 4px a 8px, como o selo do CrisDoc. A altura segue em 16px e a fonte em 10px.
42
+ - `attached`: o rótulo colado tem altura mínima de 22px (24 - 2) de qualquer tamanho; o `md` (padrão) já media 22 e
43
+ o `xs` e o `2xs` colados sobem a ele.
44
+ - `attachedTone="dark"` (novo; padrão `light`): o neutro sólido colado em cinza escuro (`neutral-dark-down`) com texto
45
+ branco, o rótulo escuro do CrisDoc. O ardósia do CrisDoc dá 2,9:1 com texto branco, então o DS usa o token que passa
46
+ (7,3:1 e inverte sozinho nos temas escuros). Só vale com `attached`, `variant="neutral"` e `appearance="solid"`.
47
+
48
+ ```vue
49
+ <Badge
50
+ attached
51
+ size="xs"
52
+ variant="neutral"
53
+ appearance="solid"
54
+ attachedTone="dark"
55
+ >Tarefa manual</Badge>
56
+ ```
57
+
58
+ - 7883c6b: feat(button): `density="comfortable"`, o `sm` com 12px de padding lateral
59
+
60
+ O botão pequeno do CrisDoc tem 12px de padding lateral e o `sm` do DS tem 8px. Mudar o `sm` alargaria todo botão que já o
61
+ usa, então a diferença virou uma densidade: `density="comfortable"` leva o `sm` a 12px (a altura de 32px, a fonte e o raio
62
+ não mudam). Sem a prop, nenhum botão muda. Nos outros tamanhos a prop não faz diferença (`xs` já usa 12px, `md` 16px e
63
+ `lg` 24px), e o botão só de ícone, de largura fixa, fica de fora.
64
+
65
+ ```vue
66
+ <Button
67
+ variant="secondary"
68
+ size="sm"
69
+ density="comfortable"
70
+ >Personalizar</Button>
71
+ ```
72
+
73
+ - c101901: feat(calendar): `density="compact"`, evento de 20px
74
+
75
+ `density` (`default` | `compact`). O `compact` leva o evento a 20px de altura (16 + 4, o degrau do DS mais perto dos 20,5 do CrisDoc;
76
+ o padrão dá 25) com 4px de padding lateral (o padrão tem 8) e linha de 16px. Com menos padding o título cabe inteiro onde antes
77
+ cortava; ele só é cortado, com reticências, quando passa da largura da célula. O padrão não muda.
78
+
79
+ ```vue
80
+ <Calendar :events="eventos" density="compact" />
81
+ ```
82
+
83
+ - c9c05cc: feat(coachmark): com um passo só, sem o contador "1 de 1"
84
+
85
+ O contador ("2 de 8") só aparece com mais de um passo, no cartão e na região `aria-live`: com um passo o leitor de
86
+ tela ouve só o título. É o caso da dica pontual (`modal={false}`) e de qualquer tour de um passo. `counterLabel`
87
+ continua valendo para os tours com mais passos. O botão do último passo segue sendo "Concluir" (`finishLabel`).
88
+
89
+ - ad47648: feat(datatable): `density="compact"` com cabeçalho e linha de 44px, e tom por linha (`rowTone`)
90
+
91
+ - `density="compact"`: `th` e `td` ganham `height` igual à altura de campo do DS (44px), no lugar dos 38 e 49 de
92
+ antes. É mínimo: conteúdo mais alto (avatar, botão) estica a linha. A tabela de documento (`variant="document"`)
93
+ continua justa.
94
+ - `rowTone`: o nome do campo da linha com o tom, ou uma função `(row, rowIndex) => tom`. Tons: `negative`,
95
+ `positive`, `info` (fundo claro da cor semântica) e `muted` (texto esmaecido). A linha selecionada ou destacada
96
+ vence o tom e o tom vence a zebra. Tom desconhecido é ignorado. O tom é decoração: o estado também vai em texto.
97
+
98
+ ```vue
99
+ <DataTable
100
+ density="compact"
101
+ :columns="colunas"
102
+ :data="usuarios"
103
+ :rowTone="
104
+ (u) => (u.bloqueado ? 'negative' : u.desligado ? 'muted' : undefined)
105
+ "
106
+ />
107
+ ```
108
+
109
+ - a1de816: feat(input): `variant="ghost"`, a busca dentro de um painel
110
+
111
+ `variant` (`default` | `ghost`). O `ghost` tira a borda escura do repouso: fundo cinza claro (`background-light`) e borda clara
112
+ (`neutral-light-medium`). Só o repouso muda: hover (borda mais escura), foco (borda azul e anel), erro, desabilitado e somente leitura
113
+ são os do campo padrão, e o ícone, o placeholder e o cursor seguem dizendo que é um campo. Sem a prop, nada muda.
114
+
115
+ ```vue
116
+ <Input
117
+ variant="ghost"
118
+ size="sm"
119
+ fullWidth
120
+ ariaLabel="Buscar filtros"
121
+ placeholder="Buscar filtros"
122
+ />
123
+ ```
124
+
125
+ - 9e41dc1: feat(modal): `density="1doc"`, as proporções do modal do CrisDoc
126
+
127
+ `density` (`default` | `1doc`) leva o cabeçalho a 67px (o padrão dá 61), o rodapé a 65px (o padrão dá 73, com o padding de 12px 24px)
128
+ e o raio a 12px (o padrão é 8px). As alturas são mínimas: título com subtítulo ou botão mais alto continua crescendo. Combina com
129
+ `variant="primary"`. A tela cheia e o painel (`responsive`) mantêm os cantos deles. Sem a prop, o modal não muda. Sem token para
130
+ essas medidas: literais comentados no CSS.
131
+
132
+ ```vue
133
+ <Modal
134
+ :open="aberto"
135
+ density="1doc"
136
+ variant="primary"
137
+ title="Resolver tarefa"
138
+ width="600px"
139
+ />
140
+ ```
141
+
142
+ - 7d9e687: feat(segmentedcontrol): `variant="surface"`, o escolhido em superfície branca
143
+
144
+ `variant` (`default` | `surface`). No `surface` o trilho é cinza claro, sem divisores, e o segmento escolhido é uma peça branca com
145
+ sombra, texto escuro e negrito (o seletor do CrisDoc). A seleção não depende só do tom: o peso e a sombra a dizem. O contador do
146
+ escolhido fica cinza sobre o branco e o dos outros, branco sobre o trilho. O padrão (azul cheio) não muda.
147
+
148
+ ```vue
149
+ <SegmentedControl
150
+ label="Modo"
151
+ variant="surface"
152
+ :options="opcoes"
153
+ :value="modo"
154
+ :onChange="trocar"
155
+ />
156
+ ```
157
+
158
+ - bf81dfa: feat(select): a lista do filtro tem a largura do gatilho, com `listMinWidth` e sem passar da janela
159
+
160
+ Regressão do 2.10 no `Select variant="filter"` (a lista do Marcadores da inbox media 556px e passava 17px da borda):
161
+
162
+ - Causa: o wrapper do filtro tinha `width: auto` e é `display: flex` (bloco), então se esticava até a largura do pai;
163
+ o `100%` da lista passava a ser a do pai e não a do gatilho. Agora é `fit-content`.
164
+ - A lista tem a largura do gatilho (nunca menor), cresce até o conteúdo e fica dentro da janela: o campo de busca
165
+ não entra mais na conta da largura (`contain: inline-size`) e acompanha a lista.
166
+ - Perto da borda direita a lista se alinha ao fim do gatilho (medido ao abrir). Na lista flutuante dentro de
167
+ diálogo, o floating-ui ganhou `shift` com 8px de respiro.
168
+ - `listMinWidth` (número em px ou medida CSS) troca o mínimo de 240px: o CrisDoc usa 220.
169
+
170
+ ```vue
171
+ <Select
172
+ variant="filter"
173
+ ariaLabel="Marcadores"
174
+ placeholder="Marcadores"
175
+ searchable
176
+ :listMinWidth="220"
177
+ :options="marcadores"
178
+ />
179
+ ```
180
+
181
+ - cfe3687: feat(select): `item` com o escopo da opção, na lista e no gatilho
182
+
183
+ O conteúdo de cada opção agora recebe a opção nos dois frameworks:
184
+
185
+ - Vue: o slot `#item="{ optionData }"` (o Mitosis gerava `<slot name="item">` sem escopo e o `optionData` chegava `undefined`; o
186
+ `postbuild.js` passa `optionData` e o apelido `option`).
187
+ - React: `item` pode ser uma função `({ optionData }) => nó` (antes, a função virava filho inválido). O nó solto, sem escopo,
188
+ continua valendo na lista.
189
+ - Com a opção escolhida (seleção única), o gatilho mostra o conteúdo do `item` no lugar do texto: o "SeletorComEtiqueta" do
190
+ CrisDoc, com nome e etiqueta, aparece igual na lista e no campo. Sem valor (placeholder), na seleção múltipla e com o nó solto do
191
+ React, o gatilho segue com o texto.
192
+
193
+ ```vue
194
+ <Select label="Prioridade" :options="opcoes" :value="valor" :onChange="mudar">
195
+ <template #item="{ optionData }"><strong>{{ optionData.label }}</strong> {{ optionData.etiqueta }}</template>
196
+ </Select>
197
+ ```
198
+
199
+ - 53dfebe: feat(statcard): `size="sm"`, o KPI com o número a 18px/800
200
+
201
+ Os KPIs da home do CrisDoc têm o número a 18px/800. `size="sm"` leva o número a esse tamanho (o `md`, padrão, segue
202
+ em 24/700). Vale também com `labelPosition="top"`, que tem o número em 32/800: o `sm` vence. O 18px não está na escala
203
+ de fonte do DS (que vai de 16 a 20) e fica como literal comentado, como o 10px do `Badge` 2xs. O número continua antes
204
+ do rótulo no HTML.
205
+
206
+ ```vue
207
+ <StatCard
208
+ size="sm"
209
+ label="Minhas tarefas"
210
+ :value="12"
211
+ :icon="faClock"
212
+ variant="primary"
213
+ />
214
+ ```
215
+
216
+ - 4471b90: feat(tabs): `density="compact"`, abas de 40px
217
+
218
+ O `Tabs` ganha `density` (`default` | `compact`). O `compact` leva a altura mínima das abas de 48px a 40px (o degrau `sm` do DS) com
219
+ padding vertical de 8px, para barras de abas dentro de uma tela (o CrisDoc usa 40). A pílula do contador (`count`) não muda e já
220
+ era o que o CrisDoc pede: cinza (`neutral-light-medium`) na aba inativa e azul só na ativa, nas duas densidades. Sem a prop, nenhuma
221
+ aba muda.
222
+
223
+ ```vue
224
+ <Tabs :value="aba" :onChange="trocar" density="compact" ariaLabel="Processos">
225
+ <Tab label="Fluxos" index="1" :count="56" />
226
+ </Tabs>
227
+ ```
228
+
229
+ ### Patch Changes
230
+
231
+ - 7ccb05c: fix(tag): `variant="positive"` outlined com o texto em `positive-dark`
232
+
233
+ O contorno da tag outlined é transparente, então o texto cai sobre o fundo de quem está atrás, que muitas vezes é
234
+ tingido (o azul-claro de um card). `positive-pure` sobre `primary-light` dá 3,6:1 e reprova o WCAG 1.4.3;
235
+ `positive-dark` dá 7,9:1 e é o mesmo par de texto da tag `soft`. Nos temas escuro e de alto contraste o `dark` também
236
+ passa de 4,5:1. O contorno continua em `positive-pure`. As outras variantes não mudam.
237
+
3
238
  ## 2.10.0
4
239
 
5
240
  ### Minor Changes
@@ -14,6 +14,8 @@ 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'
19
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.
@@ -24,6 +26,10 @@ export interface AccordionItemData {
24
26
  export interface AccordionProps {
25
27
  items: AccordionItemData[];
26
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;
27
33
  variant?: "default" | "bordered" | "separated";
28
34
  /** Título dos itens. `neutral`: 14px na cor do texto, peso médio. @default 'default' */
29
35
  titleVariant?: "default" | "neutral";
@@ -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 openIds.includes(id);
18
+ return currentOpen().includes(id);
14
19
  }
15
20
  function triggerId(id) {
16
21
  return `umds-accordion-trigger-${id}`;
@@ -21,15 +26,22 @@ function Accordion(props) {
21
26
  function toggle(id, disabled) {
22
27
  if (disabled)
23
28
  return;
24
- if (openIds.includes(id)) {
25
- setOpenIds(openIds.filter((x) => x !== id));
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
- setOpenIds([...openIds, id]);
35
+ proximos = [...atuais, id];
29
36
  }
30
37
  else {
31
- setOpenIds([id]);
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 [
@@ -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). A fonte de
64
- 10px não está na escala do DS (menor degrau: 12px); é o selo compacto do
65
- CrisDoc, sem token equivalente. */
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-quarck);
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,6 +252,8 @@
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
  }
257
259
 
@@ -267,3 +269,10 @@
267
269
  background-color: var(--umds-semantic-color-neutral-light-medium);
268
270
  color: var(--umds-semantic-color-neutral-dark-pure);
269
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,8 +13,8 @@ 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) para selo de
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";
@@ -35,6 +35,13 @@ export interface BadgeProps {
35
35
  * @default 'left'
36
36
  */
37
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";
38
45
  /**
39
46
  * Estilo visual do badge.
40
47
  * @default 'soft'
@@ -11,6 +11,9 @@ function Badge(props) {
11
11
  props.attached && props.attachedSide === "right"
12
12
  ? "umds-badge--attached-right"
13
13
  : "",
14
+ props.attached && props.attachedTone === "dark"
15
+ ? "umds-badge--attached-dark"
16
+ : "",
14
17
  props.className,
15
18
  ]
16
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
  }
@@ -65,6 +65,7 @@ export interface CalendarDayCell {
65
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.
66
66
  * @property {number} weekStartsOn - Primeiro dia da semana: 0 domingo (padrão) | 1 segunda
67
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.
68
69
  * @property {string} locale - Idioma dos nomes de mês e de dia (padrão 'pt-BR')
69
70
  * @property {string} ariaLabel - Nome acessível da grade (padrão: o título, ex.: "Maio 2026")
70
71
  * @property {string} previousLabel - Nome do botão de voltar (padrão "Anterior")
@@ -99,6 +100,8 @@ export interface CalendarProps {
99
100
  today?: string;
100
101
  weekStartsOn?: 0 | 1;
101
102
  maxVisible?: number;
103
+ /** `compact`: evento de 20px com 4px de padding lateral. @default 'default' */
104
+ density?: "default" | "compact";
102
105
  locale?: string;
103
106
  ariaLabel?: string;
104
107
  previousLabel?: string;
@@ -100,7 +100,12 @@ function Calendar(props) {
100
100
  ];
101
101
  }
102
102
  function rootClasses() {
103
- return ["umds-calendar", "umds-calendar--" + viewValue(), props.className]
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
  }
@@ -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
@@ -62,7 +62,12 @@ function Coachmark(props) {
62
62
  .replace("{current}", String(index() + 1))
63
63
  .replace("{total}", String(stepCount()));
64
64
  }
65
+ function hasCounter() {
66
+ return stepCount() > 1;
67
+ }
65
68
  function announceText() {
69
+ if (!hasCounter())
70
+ return currentStep().title;
66
71
  return counterText() + ": " + currentStep().title;
67
72
  }
68
73
  function nextText() {
@@ -357,6 +362,6 @@ function Coachmark(props) {
357
362
  desligarEsc();
358
363
  };
359
364
  }, []);
360
- 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() }), _jsx(Text, { as: "span", variant: "body2", weight: "bold", color: "primary", "aria-hidden": "true", children: counterText() }), _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 }));
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 }));
361
366
  }
362
367
  export default Coachmark;
@@ -1,6 +1,6 @@
1
1
  /* ─────────────────────────────────────────────
2
2
  CODE BLOCK
3
- Texto em fonte monoespaçada, com borda, rolagem e botão de copiar.
3
+ Texto na fonte do DS (a base, sem fonte de código), com borda, rolagem e botão de copiar.
4
4
 
5
5
  Fundo claro e texto escuro (contraste de texto, nos temas claro e escuro: os
6
6
  dois vêm dos semânticos). Sem cor de sintaxe.
@@ -28,7 +28,7 @@
28
28
  }
29
29
 
30
30
  .umds-code-block__language {
31
- font-family: var(--umds-semantic-font-family-mono);
31
+ font-family: var(--umds-semantic-font-family-base);
32
32
  font-size: var(--umds-primitive-font-size-xxxs); /* 12px */
33
33
  font-weight: var(--umds-primitive-font-weight-semibold);
34
34
  color: var(--umds-semantic-color-neutral-dark-down);
@@ -65,7 +65,7 @@
65
65
  margin: 0;
66
66
  padding: var(--umds-primitive-spacing-mini) var(--umds-primitive-spacing-xxxs); /* 12px 16px */
67
67
  overflow: auto;
68
- font-family: var(--umds-semantic-font-family-mono);
68
+ font-family: var(--umds-semantic-font-family-base);
69
69
  font-size: var(--umds-primitive-font-size-xxs); /* 14px */
70
70
  line-height: var(--umds-primitive-font-line-height-lg);
71
71
  color: var(--umds-semantic-color-neutral-dark-pure);
@@ -1,6 +1,6 @@
1
1
  import './CodeBlock.css';
2
2
  /**
3
- * CodeBlock — trecho de código (ou JSON, ou um log) em fonte monoespaçada, com
3
+ * CodeBlock — trecho de código (ou JSON, ou um log) com a fonte do DS, com
4
4
  * borda, rolagem e botão de copiar.
5
5
  *
6
6
  * O texto vem por `code` ou como conteúdo (`<CodeBlock>{...}</CodeBlock>`); o
@@ -25,7 +25,7 @@ import './CodeBlock.css';
25
25
  * @property {string} ariaLabel - Nome da área de rolagem (padrão: a linguagem, ou "Código")
26
26
  * @property {string} className - Classes CSS adicionais
27
27
  * @property {function} onCopy - Chamado com o texto depois de copiar
28
- * @quandoUsar Mostrar JSON de uma resposta, um comando, um log, um exemplo de uso: texto que se lê em fonte monoespaçada e se copia.
28
+ * @quandoUsar Mostrar JSON de uma resposta, um comando, um log, um exemplo de uso: texto que se lê como foi escrito e se copia.
29
29
  * @quandoNaoUsar Para uma tecla ou atalho, use `Kbd`. Para código dentro de uma frase, use `<code>` direto. Para editar código, use um editor especializado: este bloco só mostra.
30
30
  */
31
31
  export interface CodeBlockProps {
@@ -207,6 +207,14 @@
207
207
  padding: var(--umds-primitive-spacing-nano) var(--umds-primitive-spacing-nano); /* 6px: candidato a novo token; 8px */
208
208
  }
209
209
 
210
+ /* Cabeçalho e linha de 44px (a altura de campo do DS). `height` na célula vale como mínimo: conteúdo mais
211
+ alto (avatar, botão) estica a linha. O documento imprime justo e fica fora. */
212
+ .umds-datatable--compact:not(.umds-datatable--document) .umds-datatable__th,
213
+ .umds-datatable--compact:not(.umds-datatable--document) .umds-datatable__td {
214
+ box-sizing: border-box;
215
+ height: var(--umds-semantic-field-height);
216
+ }
217
+
210
218
  .umds-datatable--compact .umds-datatable__th--checkbox,
211
219
  .umds-datatable--compact .umds-datatable__td--checkbox {
212
220
  padding: var(--umds-primitive-spacing-nano) var(--umds-primitive-spacing-quarck) var(--umds-primitive-spacing-nano) var(--umds-primitive-spacing-nano); /* 6px: candidato a novo token; 4px; 8px */
@@ -273,6 +281,40 @@
273
281
  background: var(--umds-semantic-color-primary-light);
274
282
  }
275
283
 
284
+ /* ── Tom por linha (rowTone) ──
285
+ Fundo claro da cor semântica; a linha selecionada ou destacada vence o tom (por isso o :not). Fica depois da
286
+ zebra para vencê-la. `muted` só esmaece o texto. */
287
+ .umds-datatable__row--negative:not(.umds-datatable__row--selected),
288
+ .umds-datatable__row--negative:not(.umds-datatable__row--selected).umds-datatable__row--striped {
289
+ background: var(--umds-semantic-color-feedback-negative-light);
290
+ }
291
+
292
+ .umds-datatable__row--positive:not(.umds-datatable__row--selected),
293
+ .umds-datatable__row--positive:not(.umds-datatable__row--selected).umds-datatable__row--striped {
294
+ background: var(--umds-semantic-color-feedback-positive-light);
295
+ }
296
+
297
+ .umds-datatable__row--info:not(.umds-datatable__row--selected),
298
+ .umds-datatable__row--info:not(.umds-datatable__row--selected).umds-datatable__row--striped {
299
+ background: var(--umds-semantic-color-feedback-info-light);
300
+ }
301
+
302
+ .umds-datatable__row--negative:not(.umds-datatable__row--selected):hover {
303
+ background: color-mix(in srgb, var(--umds-semantic-color-feedback-negative-light) 85%, var(--umds-semantic-color-background-white));
304
+ }
305
+
306
+ .umds-datatable__row--positive:not(.umds-datatable__row--selected):hover {
307
+ background: color-mix(in srgb, var(--umds-semantic-color-feedback-positive-light) 85%, var(--umds-semantic-color-background-white));
308
+ }
309
+
310
+ .umds-datatable__row--info:not(.umds-datatable__row--selected):hover {
311
+ background: color-mix(in srgb, var(--umds-semantic-color-feedback-info-light) 85%, var(--umds-semantic-color-background-white));
312
+ }
313
+
314
+ .umds-datatable__row--muted .umds-datatable__td {
315
+ color: var(--umds-semantic-color-neutral-dark-down); /* #565656: 7,3:1 sobre branco */
316
+ }
317
+
276
318
  /* ── Linha destacada (selectedRowKeys / isRowSelected) ──
277
319
  Além do fundo, uma barra de 4px à esquerda: o destaque não depende só da cor. */
278
320
  .umds-datatable__row--highlighted > :first-child {
@@ -35,8 +35,13 @@ export interface DataTableColumn {
35
35
  * @property {Array<string|number>} selectedKeys - Chaves das linhas selecionadas (controlado;
36
36
  * requer rowKey)
37
37
  * @property {string} variant - 'default' (tabela de interface) | 'document' (tabela de CONTEÚDO de documento: sem ordenação, seleção nem clique de linha, bordas finas, cabeçalho repetido e linha inteira por página na impressão)
38
- * @property {string} density - Densidade das células: 'default' | 'compact'. Com `variant="document"`,
39
- * o compacto também leva a fonte a 12px (a escala do DS não tem 13px).
38
+ * @property {string} density - Densidade das células: 'default' | 'compact'. O compacto aperta o padding e
39
+ * fixa a altura do cabeçalho e da linha em 44px (a altura de campo do DS; o conteúdo maior que isso cresce a
40
+ * linha). Com `variant="document"`, o compacto também leva a fonte a 12px (a escala do DS não tem 13px).
41
+ * @property {string|function} rowTone - Tom por linha: nome do campo da linha que traz o tom, ou função
42
+ * `(row, rowIndex) => tom`. Tons: 'negative' (vermelho claro), 'positive' (verde claro), 'info' (laranja claro)
43
+ * e 'muted' (texto esmaecido). Sem tom, a linha segue o padrão; a linha selecionada ou destacada vence o tom.
44
+ * O tom é decoração: diga o estado também em texto ou ícone (a cor sozinha não basta).
40
45
  * @property {string} headerVariant - Cabeçalho: 'default' (claro) | 'dark' (navy, texto claro; o cabeçalho
41
46
  * escuro do CrisDoc). Cores pelos tokens `background-dark` e `neutral-light-pure`, então o tema acompanha.
42
47
  * @property {boolean} headerUppercase - Rótulos do cabeçalho em caixa alta. Opt-in; vale para qualquer
@@ -98,6 +103,8 @@ export interface DataTableProps {
98
103
  /** `document`: tabela de conteúdo de documento, imprimível e sem interação. @default 'default' */
99
104
  variant?: "default" | "document";
100
105
  density?: "default" | "compact";
106
+ /** Tom por linha: nome do campo ou função `(row, rowIndex) => 'negative' | 'positive' | 'info' | 'muted'`. */
107
+ rowTone?: string | ((row: Record<string, any>, rowIndex: number) => string | undefined);
101
108
  /** `dark`: cabeçalho navy com texto claro (o do CrisDoc). @default 'default' */
102
109
  headerVariant?: "default" | "dark";
103
110
  /** Rótulos do cabeçalho em caixa alta. @default false */