@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.
Files changed (172) hide show
  1. package/CHANGELOG.md +653 -0
  2. package/dist/components/Accordion/Accordion.css +13 -0
  3. package/dist/components/Accordion/Accordion.d.ts +9 -0
  4. package/dist/components/Accordion/Accordion.js +18 -5
  5. package/dist/components/AppSubheader/AppSubheader.css +10 -1
  6. package/dist/components/AttachmentCard/AttachmentCard.css +10 -8
  7. package/dist/components/AttachmentCard/AttachmentCard.d.ts +3 -0
  8. package/dist/components/AttachmentCard/AttachmentCard.js +8 -1
  9. package/dist/components/Badge/Badge.css +27 -5
  10. package/dist/components/Badge/Badge.d.ts +20 -4
  11. package/dist/components/Badge/Badge.js +6 -0
  12. package/dist/components/Button/Button.css +5 -0
  13. package/dist/components/Button/Button.d.ts +3 -0
  14. package/dist/components/Button/Button.js +1 -0
  15. package/dist/components/Calendar/Calendar.css +10 -0
  16. package/dist/components/Calendar/Calendar.d.ts +10 -6
  17. package/dist/components/Calendar/Calendar.js +6 -1
  18. package/dist/components/Coachmark/Coachmark.css +10 -0
  19. package/dist/components/Coachmark/Coachmark.d.ts +14 -1
  20. package/dist/components/Coachmark/Coachmark.js +51 -5
  21. package/dist/components/ColorPicker/ColorPicker.css +8 -1
  22. package/dist/components/ColorPicker/ColorPicker.d.ts +2 -2
  23. package/dist/components/DataTable/DataTable.css +216 -0
  24. package/dist/components/DataTable/DataTable.d.ts +70 -2
  25. package/dist/components/DataTable/DataTable.js +293 -6
  26. package/dist/components/DescriptionList/DescriptionList.css +14 -0
  27. package/dist/components/DescriptionList/DescriptionList.d.ts +3 -0
  28. package/dist/components/DescriptionList/DescriptionList.js +1 -0
  29. package/dist/components/EmptyState/EmptyState.d.ts +4 -2
  30. package/dist/components/EmptyState/EmptyState.js +2 -2
  31. package/dist/components/Input/Input.css +30 -0
  32. package/dist/components/Input/Input.d.ts +10 -0
  33. package/dist/components/Input/Input.js +3 -1
  34. package/dist/components/List/ListItem.d.ts +14 -2
  35. package/dist/components/List/ListItem.js +3 -2
  36. package/dist/components/Modal/Modal.css +19 -0
  37. package/dist/components/Modal/Modal.d.ts +7 -0
  38. package/dist/components/Modal/Modal.js +1 -0
  39. package/dist/components/RichTextEditor/RichTextEditor.css +47 -0
  40. package/dist/components/RichTextEditor/RichTextEditor.d.ts +22 -8
  41. package/dist/components/RichTextEditor/RichTextEditor.js +460 -3
  42. package/dist/components/SegmentedControl/SegmentedControl.css +70 -0
  43. package/dist/components/SegmentedControl/SegmentedControl.d.ts +5 -0
  44. package/dist/components/SegmentedControl/SegmentedControl.js +5 -1
  45. package/dist/components/Select/Select.css +122 -2
  46. package/dist/components/Select/Select.d.ts +33 -4
  47. package/dist/components/Select/Select.js +256 -35
  48. package/dist/components/StatCard/StatCard.css +73 -0
  49. package/dist/components/StatCard/StatCard.d.ts +12 -0
  50. package/dist/components/StatCard/StatCard.js +11 -1
  51. package/dist/components/Tabs/Tabs.css +11 -0
  52. package/dist/components/Tabs/Tabs.d.ts +7 -0
  53. package/dist/components/Tabs/Tabs.js +7 -1
  54. package/dist/components/Tag/Tag.css +3 -1
  55. package/dist/components/Toast/Toast.css +32 -0
  56. package/dist/components/Toast/Toast.d.ts +18 -0
  57. package/dist/components/Toast/Toast.js +14 -1
  58. package/dist/components/Toast/ToastStack.js +8 -1
  59. package/dist/teste/AccordionControlado.test.d.ts +1 -0
  60. package/dist/teste/AccordionControlado.test.js +73 -0
  61. package/dist/teste/AccordionTituloNeutro.test.d.ts +1 -0
  62. package/dist/teste/AccordionTituloNeutro.test.js +23 -0
  63. package/dist/teste/AppSubheaderRegiao.test.d.ts +1 -0
  64. package/dist/teste/AppSubheaderRegiao.test.js +13 -0
  65. package/dist/teste/AttachmentCardLista.test.d.ts +1 -0
  66. package/dist/teste/AttachmentCardLista.test.js +49 -0
  67. package/dist/teste/AttachmentCardSm.test.d.ts +1 -0
  68. package/dist/teste/AttachmentCardSm.test.js +26 -0
  69. package/dist/teste/BadgeAttachedTomEPadding.test.d.ts +1 -0
  70. package/dist/teste/BadgeAttachedTomEPadding.test.js +43 -0
  71. package/dist/teste/BadgeNeutroColado.test.d.ts +1 -0
  72. package/dist/teste/BadgeNeutroColado.test.js +11 -0
  73. package/dist/teste/BadgeTag.test.js +13 -0
  74. package/dist/teste/ButtonDensidade.test.d.ts +1 -0
  75. package/dist/teste/ButtonDensidade.test.js +28 -0
  76. package/dist/teste/CalendarCabecalho.test.d.ts +1 -0
  77. package/dist/teste/CalendarCabecalho.test.js +11 -0
  78. package/dist/teste/CalendarCompacto.test.d.ts +1 -0
  79. package/dist/teste/CalendarCompacto.test.js +29 -0
  80. package/dist/teste/CoachmarkSemVeu.test.d.ts +1 -0
  81. package/dist/teste/CoachmarkSemVeu.test.js +85 -0
  82. package/dist/teste/CoachmarkUmPasso.test.d.ts +1 -0
  83. package/dist/teste/CoachmarkUmPasso.test.js +34 -0
  84. package/dist/teste/ColorPickerXs.test.d.ts +1 -0
  85. package/dist/teste/ColorPickerXs.test.js +21 -0
  86. package/dist/teste/DataTableArvoreEVariantes.test.d.ts +1 -0
  87. package/dist/teste/DataTableArvoreEVariantes.test.js +190 -0
  88. package/dist/teste/DataTableCompactaETom.test.d.ts +1 -0
  89. package/dist/teste/DataTableCompactaETom.test.js +69 -0
  90. package/dist/teste/DescriptionListCompacta.test.d.ts +1 -0
  91. package/dist/teste/DescriptionListCompacta.test.js +22 -0
  92. package/dist/teste/EmptyStateTituloRico.test.d.ts +1 -0
  93. package/dist/teste/EmptyStateTituloRico.test.js +23 -0
  94. package/dist/teste/InputAgrupado.test.d.ts +1 -0
  95. package/dist/teste/InputAgrupado.test.js +24 -0
  96. package/dist/teste/InputGhost.test.d.ts +1 -0
  97. package/dist/teste/InputGhost.test.js +32 -0
  98. package/dist/teste/ListItemTooltipStandalone.test.d.ts +1 -0
  99. package/dist/teste/ListItemTooltipStandalone.test.js +39 -0
  100. package/dist/teste/ModalProporcoes1Doc.test.d.ts +1 -0
  101. package/dist/teste/ModalProporcoes1Doc.test.js +32 -0
  102. package/dist/teste/RichTextEditorAlinhaImagemTabela.test.d.ts +1 -0
  103. package/dist/teste/RichTextEditorAlinhaImagemTabela.test.js +212 -0
  104. package/dist/teste/RichTextEditorEstiloDeTexto.test.d.ts +1 -0
  105. package/dist/teste/RichTextEditorEstiloDeTexto.test.js +120 -0
  106. package/dist/teste/SegmentedControlContador.test.d.ts +1 -0
  107. package/dist/teste/SegmentedControlContador.test.js +39 -0
  108. package/dist/teste/SegmentedControlSuperficie.test.d.ts +1 -0
  109. package/dist/teste/SegmentedControlSuperficie.test.js +39 -0
  110. package/dist/teste/SelectAbertoControladoEFlutuante.test.d.ts +1 -0
  111. package/dist/teste/SelectAbertoControladoEFlutuante.test.js +145 -0
  112. package/dist/teste/SelectFiltroCorETamanho.test.d.ts +1 -0
  113. package/dist/teste/SelectFiltroCorETamanho.test.js +77 -0
  114. package/dist/teste/SelectItemComEscopo.test.d.ts +1 -0
  115. package/dist/teste/SelectItemComEscopo.test.js +54 -0
  116. package/dist/teste/SelectListaFiltro.test.d.ts +1 -0
  117. package/dist/teste/SelectListaFiltro.test.js +102 -0
  118. package/dist/teste/StatCardSecundariaETom.test.d.ts +1 -0
  119. package/dist/teste/StatCardSecundariaETom.test.js +45 -0
  120. package/dist/teste/StatCardTamanhoSm.test.d.ts +1 -0
  121. package/dist/teste/StatCardTamanhoSm.test.js +32 -0
  122. package/dist/teste/TabsCompacta.test.d.ts +1 -0
  123. package/dist/teste/TabsCompacta.test.js +35 -0
  124. package/dist/teste/TagPositivoTingido.test.d.ts +1 -0
  125. package/dist/teste/TagPositivoTingido.test.js +54 -0
  126. package/dist/teste/ToastAcaoEDetalhes.test.d.ts +1 -0
  127. package/dist/teste/ToastAcaoEDetalhes.test.js +94 -0
  128. package/dist/teste/lerArquivo.d.ts +1 -0
  129. package/dist/teste/lerArquivo.js +12 -0
  130. package/dist/utils/html-seguro.d.ts +19 -1
  131. package/dist/utils/html-seguro.js +113 -2
  132. package/dist/utils/toast.d.ts +10 -1
  133. package/dist/utils/toast.js +2 -1
  134. package/package.json +3 -3
  135. package/src/teste/AccordionControlado.test.tsx +87 -0
  136. package/src/teste/AccordionTituloNeutro.test.tsx +25 -0
  137. package/src/teste/AppSubheaderRegiao.test.tsx +17 -0
  138. package/src/teste/AttachmentCardLista.test.tsx +53 -0
  139. package/src/teste/AttachmentCardSm.test.tsx +29 -0
  140. package/src/teste/BadgeAttachedTomEPadding.test.tsx +60 -0
  141. package/src/teste/BadgeNeutroColado.test.tsx +15 -0
  142. package/src/teste/BadgeTag.test.tsx +21 -0
  143. package/src/teste/ButtonDensidade.test.tsx +40 -0
  144. package/src/teste/CalendarCabecalho.test.tsx +11 -0
  145. package/src/teste/CalendarCompacto.test.tsx +37 -0
  146. package/src/teste/CoachmarkSemVeu.test.tsx +101 -0
  147. package/src/teste/CoachmarkUmPasso.test.tsx +45 -0
  148. package/src/teste/ColorPickerXs.test.tsx +22 -0
  149. package/src/teste/DataTableArvoreEVariantes.test.tsx +240 -0
  150. package/src/teste/DataTableCompactaETom.test.tsx +85 -0
  151. package/src/teste/DescriptionListCompacta.test.tsx +24 -0
  152. package/src/teste/EmptyStateTituloRico.test.tsx +34 -0
  153. package/src/teste/InputAgrupado.test.tsx +26 -0
  154. package/src/teste/InputGhost.test.tsx +42 -0
  155. package/src/teste/ListItemTooltipStandalone.test.tsx +64 -0
  156. package/src/teste/ModalProporcoes1Doc.test.tsx +36 -0
  157. package/src/teste/RichTextEditorAlinhaImagemTabela.test.tsx +238 -0
  158. package/src/teste/RichTextEditorEstiloDeTexto.test.tsx +137 -0
  159. package/src/teste/SegmentedControlContador.test.tsx +43 -0
  160. package/src/teste/SegmentedControlSuperficie.test.tsx +52 -0
  161. package/src/teste/SelectAbertoControladoEFlutuante.test.tsx +180 -0
  162. package/src/teste/SelectFiltroCorETamanho.test.tsx +90 -0
  163. package/src/teste/SelectItemComEscopo.test.tsx +69 -0
  164. package/src/teste/SelectListaFiltro.test.tsx +120 -0
  165. package/src/teste/StatCardSecundariaETom.test.tsx +51 -0
  166. package/src/teste/StatCardTamanhoSm.test.tsx +42 -0
  167. package/src/teste/TabsCompacta.test.tsx +45 -0
  168. package/src/teste/TagPositivoTingido.test.tsx +60 -0
  169. package/src/teste/ToastAcaoEDetalhes.test.tsx +109 -0
  170. package/src/teste/lerArquivo.ts +13 -0
  171. package/src/utils/html-seguro.ts +105 -2
  172. package/src/utils/toast.ts +12 -2
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import './Select.css';
4
4
  import { useState, useRef, useEffect } from "react";
5
5
  import { faChevronUp, faChevronDown, faMagnifyingGlass, faXmark, faCheck, faTrash, } from "@fortawesome/free-solid-svg-icons";
6
+ import { computePosition, autoUpdate, offset, flip, shift, size, } from "@floating-ui/dom";
6
7
  import Text from "../Text/Text";
7
8
  import Icon from "../Icon/Icon";
8
9
  import Spinner from "../Spinner/Spinner";
@@ -15,6 +16,10 @@ function Select(props) {
15
16
  const searchInputRef = useRef(null);
16
17
  const outsideWatcherRef = useRef(null);
17
18
  const searchTimeoutRef = useRef(null);
19
+ const dropdownRef = useRef(null);
20
+ const floatCleanupRef = useRef(null);
21
+ const latestOpenRef = useRef(false);
22
+ const latestOpenChangeRef = useRef(null);
18
23
  function textoDeApoio() {
19
24
  return props.helperText || props.description || "";
20
25
  }
@@ -25,6 +30,15 @@ function Select(props) {
25
30
  return props.readOnly ? "true" : undefined;
26
31
  }
27
32
  const [isOpen, setIsOpen] = useState(() => false);
33
+ const [prepared, setPrepared] = useState(() => false);
34
+ const [floatMode, setFloatMode] = useState(() => false);
35
+ const [floatReady, setFloatReady] = useState(() => false);
36
+ const [floatX, setFloatX] = useState(() => 0);
37
+ const [floatY, setFloatY] = useState(() => 0);
38
+ const [floatW, setFloatW] = useState(() => 0);
39
+ const [floatMaxH, setFloatMaxH] = useState(() => 0);
40
+ const [listMaxH, setListMaxH] = useState(() => 0);
41
+ const [alignEnd, setAlignEnd] = useState(() => false);
28
42
  const [searchTerm, setSearchTerm] = useState(() => "");
29
43
  const [internalSelected, setInternalSelected] = useState(() => Array.isArray(props.defaultValue)
30
44
  ? props.defaultValue
@@ -36,6 +50,24 @@ function Select(props) {
36
50
  const [shouldFocusTrigger, setShouldFocusTrigger] = useState(() => false);
37
51
  const [activeIndex, setActiveIndex] = useState(() => -1);
38
52
  const [uid, setUid] = useState(() => "");
53
+ function opened() {
54
+ if (props.open !== undefined)
55
+ return props.open === "" ? true : !!props.open;
56
+ return isOpen;
57
+ }
58
+ function isFilter() {
59
+ return (props.variant === "filter" ||
60
+ props.variant === "search" ||
61
+ props.variant === "combo");
62
+ }
63
+ function setOpen(next) {
64
+ if (next === opened())
65
+ return;
66
+ if (props.open === undefined)
67
+ setIsOpen(next);
68
+ if (props.onOpenChange)
69
+ props.onOpenChange(next);
70
+ }
39
71
  function baseId() {
40
72
  return props.id || uid;
41
73
  }
@@ -48,6 +80,9 @@ function Select(props) {
48
80
  function isInvalid() {
49
81
  return !!props.invalid || !!props.errorMessage;
50
82
  }
83
+ function ariaRequiredText() {
84
+ return props.required ? "true" : undefined;
85
+ }
51
86
  function ariaInvalidText() {
52
87
  return isInvalid() ? "true" : undefined;
53
88
  }
@@ -116,6 +151,28 @@ function Select(props) {
116
151
  }
117
152
  return String(currentVal[0]);
118
153
  }
154
+ function opcaoDoGatilho() {
155
+ if (props.multiple || isPlaceholder())
156
+ return undefined;
157
+ return selectedOptionsList()[0];
158
+ }
159
+ function itemNoGatilho() {
160
+ return typeof props.item === "function" && !!opcaoDoGatilho();
161
+ }
162
+ function conteudoDoItemNoGatilho() {
163
+ return props.item({
164
+ optionData: opcaoDoGatilho(),
165
+ option: opcaoDoGatilho(),
166
+ });
167
+ }
168
+ function triggerSwatchColor() {
169
+ if (props.triggerSwatch)
170
+ return props.triggerSwatch;
171
+ if (props.multiple || isPlaceholder())
172
+ return undefined;
173
+ const escolhida = selectedOptionsList()[0];
174
+ return escolhida ? escolhida.color : undefined;
175
+ }
119
176
  function isPlaceholder() {
120
177
  const currentVal = props.value !== undefined
121
178
  ? Array.isArray(props.value)
@@ -162,14 +219,20 @@ function Select(props) {
162
219
  function wrapperClassesList() {
163
220
  return [
164
221
  "umds-select",
165
- `umds-select--${props.size || "md"}`,
222
+ `umds-select--${props.size || (isFilter() ? "sm" : "md")}`,
223
+ isFilter() && !isPlaceholder() ? "umds-select--has-value" : "",
166
224
  props.fullWidth ? "umds-select--full-width" : "",
167
225
  props.disabled ? "umds-select--disabled" : "",
168
226
  props.readOnly ? "umds-select--readonly" : "",
169
227
  props.invalid ? "umds-select--invalid" : "",
170
- isOpen ? "umds-select--open" : "",
228
+ opened() ? "umds-select--open" : "",
171
229
  props.searchVariant === "inline" ? "umds-select--search-inline" : "",
172
- props.variant === "filter" ? "umds-select--filter" : "",
230
+ isFilter() ? "umds-select--filter" : "",
231
+ props.variant === "search" ? "umds-select--filter-search" : "",
232
+ props.variant === "combo" ? "umds-select--filter-combo" : "",
233
+ opened() && floatMode ? "umds-select--floating" : "",
234
+ opened() && alignEnd ? "umds-select--list-end" : "",
235
+ props.triggerIcon ? "umds-select--has-trigger-icon" : "",
173
236
  opensUp && props.searchVariant !== "inline"
174
237
  ? "umds-select--placement-top"
175
238
  : "",
@@ -198,7 +261,7 @@ function Select(props) {
198
261
  return props.label && !baseId() ? props.label : undefined;
199
262
  }
200
263
  function activeDescendantText() {
201
- if (!isOpen || activeIndex < 0 || !baseId())
264
+ if (!opened() || activeIndex < 0 || !baseId())
202
265
  return undefined;
203
266
  return baseId() + "-option-" + activeIndex;
204
267
  }
@@ -208,8 +271,18 @@ function Select(props) {
208
271
  function removeItemAriaLabelText(label) {
209
272
  return (props.removeAriaLabel || "Remover") + " " + label;
210
273
  }
211
- function shouldOpenUp() {
274
+ function insideDialog() {
275
+ if (typeof document === "undefined" ||
276
+ !triggerRef.current ||
277
+ props.searchVariant === "inline")
278
+ return false;
279
+ if (typeof triggerRef.current.closest !== "function")
280
+ return false;
281
+ return !!triggerRef.current.closest('[role="dialog"], [aria-modal="true"]');
282
+ }
283
+ function shouldOpenUp(flutuante) {
212
284
  const pedido = props.placement || "bottom";
285
+ setListMaxH(0);
213
286
  if (pedido === "top")
214
287
  return true;
215
288
  if (pedido !== "auto")
@@ -217,32 +290,140 @@ function Select(props) {
217
290
  if (typeof window === "undefined" || !triggerRef.current)
218
291
  return false;
219
292
  const caixa = triggerRef.current.getBoundingClientRect();
220
- const abaixo = window.innerHeight - caixa.bottom;
221
- const acima = caixa.top;
293
+ let topo = 0;
294
+ let base = window.innerHeight;
295
+ if (!flutuante) {
296
+ let pai = triggerRef.current.parentElement;
297
+ while (pai && pai !== document.body) {
298
+ const corte = window.getComputedStyle(pai).overflowY;
299
+ if (corte === "auto" ||
300
+ corte === "scroll" ||
301
+ corte === "hidden" ||
302
+ corte === "clip") {
303
+ const area = pai.getBoundingClientRect();
304
+ topo = Math.max(topo, area.top);
305
+ base = Math.min(base, area.bottom);
306
+ }
307
+ pai = pai.parentElement;
308
+ }
309
+ }
310
+ const abaixo = base - caixa.bottom;
311
+ const acima = caixa.top - topo;
222
312
  const opcoes = filteredOptionsList().length;
223
313
  const estimada = Math.min(300, opcoes * 36 + 8 + (props.searchable ? 44 : 0) + (props.footer ? 44 : 0));
224
- return abaixo < estimada && acima > abaixo;
314
+ const sobe = abaixo < estimada && acima > abaixo;
315
+ const espaco = (sobe ? acima : abaixo) - 8;
316
+ if (espaco < 300)
317
+ setListMaxH(Math.max(96, Math.floor(espaco)));
318
+ return sobe;
319
+ }
320
+ function prepareOpen() {
321
+ setPrepared(true);
322
+ // Local: no React gerado o setState é assíncrono e o estado leria STALE.
323
+ const flutuante = insideDialog();
324
+ setFloatMode(flutuante);
325
+ setFloatReady(false);
326
+ setAlignEnd(false);
327
+ setOpensUp(shouldOpenUp(flutuante));
328
+ // Abre com a opção já selecionada ativa (padrão combobox).
329
+ setActiveIndex(filteredOptionsList().findIndex((opt) => isOptionSelected(opt.value)));
330
+ if (props.searchable &&
331
+ (props.autoFocusSearch || props.searchVariant === "inline" || isFilter())) {
332
+ setShouldFocusSearch(true);
333
+ }
334
+ }
335
+ function updateFloat() {
336
+ if (!triggerRef.current || !dropdownRef.current)
337
+ return;
338
+ computePosition(triggerRef.current, dropdownRef.current, {
339
+ strategy: "fixed",
340
+ placement: opensUp ? "top-start" : "bottom-start",
341
+ middleware: [
342
+ offset(4),
343
+ flip({
344
+ fallbackStrategy: "bestFit",
345
+ }),
346
+ // Perto da borda da janela, a lista desliza para dentro (8px de respiro).
347
+ shift({
348
+ padding: 8,
349
+ }),
350
+ size({
351
+ apply({ availableHeight, rects }) {
352
+ setFloatW(rects.reference.width);
353
+ setFloatMaxH(Math.max(96, Math.min(300, Math.floor(availableHeight - 8))));
354
+ },
355
+ }),
356
+ ],
357
+ }).then((posicao) => {
358
+ setFloatX(Math.round(posicao.x));
359
+ setFloatY(Math.round(posicao.y));
360
+ setFloatReady(true);
361
+ });
362
+ }
363
+ function listMinWidthCss() {
364
+ const pedida = props.listMinWidth;
365
+ if (!isFilter() || pedida === undefined || pedida === null || pedida === "")
366
+ return "";
367
+ return typeof pedida === "number" ? pedida + "px" : String(pedida);
368
+ }
369
+ function keepInWindow() {
370
+ if (typeof window === "undefined" || !dropdownRef.current)
371
+ return;
372
+ if (!opened() || !isFilter() || floatMode || alignEnd)
373
+ return;
374
+ const caixa = dropdownRef.current.getBoundingClientRect();
375
+ // 8px de respiro até a borda: o degrau nano do DS.
376
+ if (caixa.right > window.innerWidth - 8)
377
+ setAlignEnd(true);
378
+ }
379
+ function dropdownStyle() {
380
+ if (props.searchVariant === "inline")
381
+ return {
382
+ top: "0",
383
+ };
384
+ if (opened() && floatMode) {
385
+ const estilo = {
386
+ position: "fixed",
387
+ top: "0",
388
+ left: "0",
389
+ bottom: "auto",
390
+ transform: "translate(" + floatX + "px, " + floatY + "px)",
391
+ visibility: floatReady ? "visible" : "hidden",
392
+ };
393
+ if (isFilter()) {
394
+ estilo.minWidth =
395
+ "max(" +
396
+ floatW +
397
+ "px, " +
398
+ (listMinWidthCss() || "calc(var(--umds-primitive-spacing-xxl) * 3)") +
399
+ ")";
400
+ }
401
+ else {
402
+ estilo.width = floatW + "px";
403
+ }
404
+ if (floatMaxH)
405
+ estilo.maxHeight = floatMaxH + "px";
406
+ return estilo;
407
+ }
408
+ const estilo = {};
409
+ if (listMaxH)
410
+ estilo.maxHeight = listMaxH + "px";
411
+ if (listMinWidthCss())
412
+ estilo.minWidth = "max(100%, " + listMinWidthCss() + ")";
413
+ return estilo;
225
414
  }
226
415
  function toggle() {
227
416
  if (props.disabled || props.readOnly)
228
417
  return;
229
- // NUNCA ler isOpen logo após atribuí-lo: no React gerado o
230
- // setState é assíncrono e a leitura vem STALE. Decidir os ramos pela
231
- // variável local (mesmo padrão do toggle do Switch).
232
- const next = !isOpen;
233
- setIsOpen(next);
418
+ // Decide pela variável local: no React gerado o setState é assíncrono e
419
+ // ler o estado logo após atribuí-lo vem STALE.
420
+ const next = !opened();
421
+ setOpen(next);
234
422
  if (next) {
235
- setOpensUp(shouldOpenUp());
236
- // Abre com a opção já selecionada ativa (padrão combobox).
237
- setActiveIndex(filteredOptionsList().findIndex((opt) => isOptionSelected(opt.value)));
238
- if (props.searchable &&
239
- (props.autoFocusSearch ||
240
- props.searchVariant === "inline" ||
241
- props.variant === "filter")) {
242
- setShouldFocusSearch(true);
243
- }
423
+ prepareOpen();
244
424
  }
245
425
  else {
426
+ setPrepared(false);
246
427
  setActiveIndex(-1);
247
428
  }
248
429
  }
@@ -275,7 +456,8 @@ function Select(props) {
275
456
  }
276
457
  }
277
458
  function closeSearch() {
278
- setIsOpen(false);
459
+ setOpen(false);
460
+ setPrepared(false);
279
461
  setSearchTerm("");
280
462
  setActiveIndex(-1);
281
463
  if (props.onSearchChange) {
@@ -298,7 +480,7 @@ function Select(props) {
298
480
  return;
299
481
  const key = event.key;
300
482
  if (key === "Escape") {
301
- if (isOpen) {
483
+ if (opened()) {
302
484
  event.preventDefault();
303
485
  closeSearch();
304
486
  }
@@ -311,7 +493,7 @@ function Select(props) {
311
493
  return;
312
494
  }
313
495
  event.preventDefault();
314
- if (!isOpen) {
496
+ if (!opened()) {
315
497
  toggle();
316
498
  return;
317
499
  }
@@ -377,7 +559,8 @@ function Select(props) {
377
559
  else {
378
560
  newValue = option.value;
379
561
  setInternalSelected([newValue]);
380
- setIsOpen(false);
562
+ setOpen(false);
563
+ setPrepared(false);
381
564
  setActiveIndex(-1);
382
565
  }
383
566
  if (props.onChange) {
@@ -428,12 +611,23 @@ function Select(props) {
428
611
  useEffect(() => {
429
612
  if (!props.id)
430
613
  setUid(gerarId("umds-select"));
614
+ // No Vue o onUpdate só roda depois de uma atualização: a lista que já nasce
615
+ // aberta (open controlado) é preparada aqui.
616
+ latestOpenRef.current = opened();
617
+ latestOpenChangeRef.current = props.onOpenChange;
618
+ if (opened() && !prepared)
619
+ prepareOpen();
620
+ keepInWindow();
431
621
  if (typeof document === "undefined")
432
622
  return;
433
623
  const handleClickOutside = (event) => {
434
624
  if (selectRef.current &&
435
625
  !selectRef.current.contains(event.target)) {
626
+ // Só avisa quando a lista está aberta: o ouvinte vive no document inteiro.
627
+ if (latestOpenRef.current && latestOpenChangeRef.current)
628
+ latestOpenChangeRef.current(false);
436
629
  setIsOpen(false);
630
+ setPrepared(false);
437
631
  setActiveIndex(-1);
438
632
  }
439
633
  };
@@ -441,7 +635,30 @@ function Select(props) {
441
635
  document.addEventListener("mousedown", handleClickOutside);
442
636
  }, []);
443
637
  useEffect(() => {
444
- if (isOpen && shouldFocusSearch) {
638
+ // Guarda o estado visto neste render para o ouvinte de clique fora.
639
+ latestOpenRef.current = opened();
640
+ latestOpenChangeRef.current = props.onOpenChange;
641
+ // Aberta de fora (open controlado): aplica os efeitos de abrir uma vez.
642
+ if (opened() && !prepared)
643
+ prepareOpen();
644
+ if (!opened() && prepared)
645
+ setPrepared(false);
646
+ if (!opened() && alignEnd)
647
+ setAlignEnd(false);
648
+ keepInWindow();
649
+ // Lista flutuante: segue o gatilho (scroll, resize) enquanto aberta.
650
+ if (opened() &&
651
+ floatMode &&
652
+ triggerRef.current &&
653
+ dropdownRef.current &&
654
+ !floatCleanupRef.current) {
655
+ floatCleanupRef.current = autoUpdate(triggerRef.current, dropdownRef.current, () => updateFloat());
656
+ }
657
+ if ((!opened() || !floatMode) && floatCleanupRef.current) {
658
+ floatCleanupRef.current();
659
+ floatCleanupRef.current = null;
660
+ }
661
+ if (opened() && shouldFocusSearch) {
445
662
  // Retry a few times if ref is not yet ready (though onUpdate usually implies rendered)
446
663
  // We use a small timeout to ensure browser paints or simply check ref
447
664
  if (searchInputRef.current) {
@@ -450,7 +667,7 @@ function Select(props) {
450
667
  }
451
668
  }
452
669
  // Devolve o foco ao trigger após fechar por Esc/seleção via teclado.
453
- if (!isOpen && shouldFocusTrigger) {
670
+ if (!opened() && shouldFocusTrigger) {
454
671
  if (triggerRef.current) {
455
672
  triggerRef.current.focus();
456
673
  setShouldFocusTrigger(false);
@@ -459,6 +676,10 @@ function Select(props) {
459
676
  });
460
677
  useEffect(() => {
461
678
  return () => {
679
+ if (floatCleanupRef.current) {
680
+ floatCleanupRef.current();
681
+ floatCleanupRef.current = null;
682
+ }
462
683
  // Cancela o debounce pendente da busca — evita callback após unmount.
463
684
  if (searchTimeoutRef.current) {
464
685
  clearTimeout(searchTimeoutRef.current);
@@ -472,13 +693,11 @@ function Select(props) {
472
693
  }
473
694
  };
474
695
  }, []);
475
- return (_jsxs("div", { ref: selectRef, className: wrapperClassesList(), children: [props.label ? (_jsxs(Text, { as: "label", variant: "label", className: "umds-select__label", id: labelIdText(), children: [props.label, props.required ? (_jsx("span", { className: "umds-select__required", children: " *" })) : null] })) : null, _jsxs("div", { className: "umds-select__container", children: [!(isOpen && props.searchVariant === "inline") ? (_jsxs("div", { role: "combobox", "aria-haspopup": "listbox", className: "umds-select__trigger", ref: triggerRef, onClick: (event) => toggle(), onKeyDown: (event) => handleTriggerKeyDown(event), "aria-readonly": ariaReadonly(), "aria-expanded": isOpen, "aria-controls": listboxIdText(), "aria-activedescendant": activeDescendantText(), "aria-labelledby": comboboxLabelledBy(), "aria-label": comboboxAriaLabel(), "aria-invalid": ariaInvalidText(), "aria-describedby": describedByText(), "aria-disabled": props.disabled ? "true" : undefined, tabIndex: props.disabled ? -1 : 0, children: [_jsxs("div", { className: "umds-select__values", children: [displayLabelText() ? (_jsx(Text, { as: "span", variant: "body2", className: isPlaceholder() ? "umds-select__placeholder" : "", children: displayLabelText() })) : null, props.multiple &&
696
+ return (_jsxs("div", { ref: selectRef, className: wrapperClassesList(), children: [props.label ? (_jsxs(Text, { as: "label", variant: "label", className: "umds-select__label", id: labelIdText(), children: [props.label, props.required ? (_jsx("span", { className: "umds-select__required", children: " *" })) : null] })) : null, _jsxs("div", { className: "umds-select__container", children: [!(opened() && props.searchVariant === "inline") ? (_jsxs("div", { role: "combobox", "aria-haspopup": "listbox", className: "umds-select__trigger", ref: triggerRef, onClick: (event) => toggle(), onKeyDown: (event) => handleTriggerKeyDown(event), "aria-readonly": ariaReadonly(), "aria-expanded": opened(), "aria-controls": listboxIdText(), "aria-activedescendant": activeDescendantText(), "aria-labelledby": comboboxLabelledBy(), "aria-label": comboboxAriaLabel(), "aria-invalid": ariaInvalidText(), "aria-required": ariaRequiredText(), "aria-describedby": describedByText(), "aria-disabled": props.disabled ? "true" : undefined, tabIndex: props.disabled ? -1 : 0, children: [_jsxs("div", { className: "umds-select__values", children: [props.triggerIcon ? (_jsx("span", { "aria-hidden": "true", className: "umds-select__trigger-icon", children: _jsx(Icon, { variant: "inherit", customSize: "12px", icon: props.triggerIcon }) })) : null, triggerSwatchColor() ? (_jsx("span", { "aria-hidden": "true", className: "umds-select__swatch", style: {
697
+ background: triggerSwatchColor(),
698
+ } })) : null, itemNoGatilho() ? (_jsx("span", { className: "umds-select__trigger-item", children: conteudoDoItemNoGatilho() })) : null, displayLabelText() && !itemNoGatilho() ? (_jsx(Text, { as: "span", variant: "body2", className: isPlaceholder() ? "umds-select__placeholder" : "", children: displayLabelText() })) : null, props.multiple &&
476
699
  props.selectionDisplay === "tags" &&
477
- selectedOptionsList().length > 0 ? (_jsx("div", { className: "umds-select__tags", children: selectedOptionsList()?.map((opt) => (_jsx(Tag, { size: "sm", label: opt.label, closable: true, disabled: props.disabled, onClose: (e) => removeValue(opt.value, e) }, opt.value))) })) : null] }), _jsxs("div", { className: "umds-select__actions", children: [props.loading ? _jsx(Spinner, { size: "sm", variant: "secondary" }) : null, _jsx("div", { className: "umds-select__arrow", children: _jsx(Icon, { size: "sm", icon: isOpen ? faChevronUp : faChevronDown }) })] })] })) : null, isOpen ? (_jsxs("div", { className: "umds-select__dropdown", style: props.searchVariant === "inline"
478
- ? {
479
- top: "0",
480
- }
481
- : {}, onKeyDown: (event) => handleDropdownKeyDown(event), children: [props.searchable ? (_jsxs("div", { className: "umds-select__search-container", children: [_jsx("div", { className: "umds-select__search-icon", children: _jsx(Icon, { size: "sm", icon: faMagnifyingGlass }) }), _jsx("input", { type: "text", className: "umds-select__search-input", ref: searchInputRef, placeholder: props.searchPlaceholder || "Buscar...", "aria-label": props.searchAriaLabel || "Buscar", value: searchTerm, onInput: (e) => handleSearch(e) }), props.searchVariant === "inline" ? (_jsx("button", { type: "button", className: "umds-select__search-close", "aria-label": props.closeSearchAriaLabel || "Fechar busca", onClick: (event) => closeSearch(), onKeyDown: (event) => handleSearchCloseKeyDown(event), children: _jsx(Icon, { size: "sm", icon: faXmark }) })) : null] })) : null, _jsxs("div", { role: "listbox", className: "umds-select__options", id: listboxIdText(), "aria-multiselectable": props.multiple ? "true" : undefined, onScroll: (e) => onScrollList(e), children: [filteredOptionsList().length === 0 && !props.loading ? (_jsx("div", { className: "umds-select__no-options", children: _jsx(Text, { variant: "body2", color: "secondary", children: props.emptyLabel || "Nenhum item encontrado" }) })) : null, filteredOptionsList()?.map((option, index) => (_jsxs("div", { role: "option", id: optionIdText(index), "aria-selected": isOptionSelected(option.value), onClick: (event) => selectOption(option), className: [
700
+ selectedOptionsList().length > 0 ? (_jsx("div", { className: "umds-select__tags", children: selectedOptionsList()?.map((opt) => (_jsx(Tag, { size: "sm", label: opt.label, closable: true, disabled: props.disabled, onClose: (e) => removeValue(opt.value, e) }, opt.value))) })) : null] }), _jsxs("div", { className: "umds-select__actions", children: [props.loading ? _jsx(Spinner, { size: "sm", variant: "secondary" }) : null, _jsx("div", { className: "umds-select__arrow", children: _jsx(Icon, { size: "sm", icon: opened() ? faChevronUp : faChevronDown }) })] })] })) : null, opened() ? (_jsxs("div", { className: "umds-select__dropdown", ref: dropdownRef, style: dropdownStyle(), onKeyDown: (event) => handleDropdownKeyDown(event), children: [props.searchable ? (_jsxs("div", { className: "umds-select__search-container", children: [_jsx("div", { className: "umds-select__search-icon", children: _jsx(Icon, { size: "sm", icon: faMagnifyingGlass }) }), _jsx("input", { type: "text", className: "umds-select__search-input", ref: searchInputRef, placeholder: props.searchPlaceholder || "Buscar...", "aria-label": props.searchAriaLabel || "Buscar", value: searchTerm, onInput: (e) => handleSearch(e) }), props.searchVariant === "inline" ? (_jsx("button", { type: "button", className: "umds-select__search-close", "aria-label": props.closeSearchAriaLabel || "Fechar busca", onClick: (event) => closeSearch(), onKeyDown: (event) => handleSearchCloseKeyDown(event), children: _jsx(Icon, { size: "sm", icon: faXmark }) })) : null] })) : null, _jsxs("div", { role: "listbox", className: "umds-select__options", id: listboxIdText(), "aria-multiselectable": props.multiple ? "true" : undefined, onScroll: (e) => onScrollList(e), children: [filteredOptionsList().length === 0 && !props.loading ? (_jsx("div", { className: "umds-select__no-options", children: _jsx(Text, { variant: "body2", color: "secondary", children: props.emptyLabel || "Nenhum item encontrado" }) })) : null, filteredOptionsList()?.map((option, index) => (_jsxs("div", { role: "option", id: optionIdText(index), "aria-selected": isOptionSelected(option.value), onClick: (event) => selectOption(option), className: [
482
701
  "umds-select__option",
483
702
  isOptionSelected(option.value)
484
703
  ? "umds-select__option--selected"
@@ -486,7 +705,9 @@ function Select(props) {
486
705
  index === activeIndex ? "umds-select__option--active" : "",
487
706
  ]
488
707
  .filter(Boolean)
489
- .join(" "), children: [props.multiple ? (_jsx("div", { className: "umds-select__option-checkbox", children: _jsx(Checkbox, { checked: isOptionSelected(option.value), decorative: true }) })) : null, _jsx("div", { className: "umds-select__option-content", children: _jsx(_Fragment, { children: props.item || (_jsx(Text, { variant: "body2", children: option.label })) }) }), !props.multiple && isOptionSelected(option.value) ? (_jsx("div", { className: "umds-select__option-check", children: _jsx(Icon, { size: "sm", icon: faCheck }) })) : null] }, option.value))), props.loading && props.pagination ? (_jsxs("div", { className: "umds-select__loading-more", children: [_jsx(Spinner, { size: "sm" }), _jsx(Text, { variant: "caption", children: props.loadingLabel || "Carregando mais..." })] })) : null] }), props.footer ? (_jsx("div", { className: "umds-select__footer", children: _jsx(_Fragment, { children: props.footer }) })) : null] })) : null] }), props.name ? (_jsx("input", { type: "hidden", name: props.name, value: formValue() || "" })) : null, props.errorMessage ? (_jsx(Text, { variant: "caption", color: "negative", className: "umds-select__error-message", id: errorIdText(), children: props.errorMessage })) : null, mostraApoio() ? (_jsx(Text, { variant: "caption", color: "secondary", className: "umds-select__description", id: helperIdText(), children: textoDeApoio() })) : null, props.multiple &&
708
+ .join(" "), children: [props.multiple ? (_jsx("div", { className: "umds-select__option-checkbox", children: _jsx(Checkbox, { checked: isOptionSelected(option.value), decorative: true }) })) : null, option.color ? (_jsx("span", { "aria-hidden": "true", className: "umds-select__swatch", style: {
709
+ background: option.color,
710
+ } })) : null, _jsx("div", { className: "umds-select__option-content", children: _jsx(_Fragment, { children: typeof props.item === "function" ? props.item({ optionData: option, option }) : props.item || (_jsx(Text, { variant: "body2", children: option.label })) }) }), !props.multiple && isOptionSelected(option.value) ? (_jsx("div", { className: "umds-select__option-check", children: _jsx(Icon, { size: "sm", icon: faCheck }) })) : null] }, option.value))), props.loading && props.pagination ? (_jsxs("div", { className: "umds-select__loading-more", children: [_jsx(Spinner, { size: "sm" }), _jsx(Text, { variant: "caption", children: props.loadingLabel || "Carregando mais..." })] })) : null] }), props.footer ? (_jsx("div", { className: "umds-select__footer", children: _jsx(_Fragment, { children: props.footer }) })) : null] })) : null] }), props.name ? (_jsx("input", { type: "hidden", name: props.name, value: formValue() || "" })) : null, props.errorMessage ? (_jsx(Text, { variant: "caption", color: "negative", className: "umds-select__error-message", id: errorIdText(), children: props.errorMessage })) : null, mostraApoio() ? (_jsx(Text, { variant: "caption", color: "secondary", className: "umds-select__description", id: helperIdText(), children: textoDeApoio() })) : null, props.multiple &&
490
711
  props.selectionDisplay === "list" &&
491
712
  selectedOptionsList().length > 0 ? (_jsx("div", { className: "umds-select__selected-list", children: selectedOptionsList()?.map((opt) => (_jsxs("div", { className: "umds-select__selected-list-item", children: [_jsx(Text, { variant: "body2", children: opt.label }), _jsx("button", { type: "button", className: "umds-select__list-remove", "aria-label": removeItemAriaLabelText(opt.label), onClick: (e) => removeValue(opt.value, e), children: _jsx(Icon, { size: "sm", icon: faTrash }) })] }, opt.value))) })) : null] }));
492
713
  }
@@ -225,3 +225,76 @@
225
225
  .umds-stat-card--rotulo-topo:not(:has(.umds-stat-card__icone)) .umds-stat-card__valor {
226
226
  grid-column: 1 / -1;
227
227
  }
228
+
229
+ /* ── valueTone: cor do número principal (variantes escuras: contraste AA no branco) ── */
230
+ .umds-stat-card--valor-primary .umds-stat-card__valor {
231
+ color: var(--umds-semantic-color-primary-pure);
232
+ }
233
+
234
+ .umds-stat-card--valor-positive .umds-stat-card__valor {
235
+ color: var(--umds-semantic-color-feedback-positive-dark);
236
+ }
237
+
238
+ .umds-stat-card--valor-negative .umds-stat-card__valor {
239
+ color: var(--umds-semantic-color-feedback-negative-dark);
240
+ }
241
+
242
+ .umds-stat-card--valor-info .umds-stat-card__valor {
243
+ color: var(--umds-semantic-color-feedback-info-dark);
244
+ }
245
+
246
+ /* ── Segunda medida (secondaryValue, secondaryLabel) ──
247
+ Menor, à direita da principal, separada por um filete. Vem depois do corpo no HTML. */
248
+ .umds-stat-card__secundaria {
249
+ display: flex;
250
+ flex-direction: column;
251
+ gap: var(--umds-primitive-spacing-micro);
252
+ margin-left: auto;
253
+ padding-left: var(--umds-primitive-spacing-xxxs);
254
+ border-left: var(--umds-primitive-border-width-hairline) solid var(--umds-semantic-color-neutral-light-medium);
255
+ text-align: right;
256
+ }
257
+
258
+ .umds-stat-card--centro .umds-stat-card__secundaria {
259
+ margin-left: 0;
260
+ padding-left: 0;
261
+ border-left: none;
262
+ align-items: center;
263
+ text-align: center;
264
+ }
265
+
266
+ .umds-stat-card__valor-secundario {
267
+ font-size: var(--umds-primitive-font-size-sm); /* 20px */
268
+ font-weight: var(--umds-primitive-font-weight-bold);
269
+ line-height: 1.15;
270
+ font-variant-numeric: tabular-nums;
271
+ color: var(--umds-semantic-color-neutral-dark-down);
272
+ }
273
+
274
+ .umds-stat-card__rotulo-secundario {
275
+ font-size: var(--umds-primitive-font-size-xxxs);
276
+ color: var(--umds-semantic-color-neutral-dark-down);
277
+ line-height: 1.4;
278
+ }
279
+
280
+ /* Com o rótulo no topo a grade ganha uma terceira coluna para a segunda medida. */
281
+ .umds-stat-card--rotulo-topo.umds-stat-card--com-secundaria {
282
+ grid-template-columns: auto 1fr auto;
283
+ }
284
+
285
+ .umds-stat-card--rotulo-topo .umds-stat-card__secundaria {
286
+ grid-column: 3;
287
+ grid-row: 2;
288
+ }
289
+
290
+ .umds-stat-card--rotulo-topo.umds-stat-card--com-secundaria:not(:has(.umds-stat-card__icone)) .umds-stat-card__valor {
291
+ grid-column: 1 / 3;
292
+ }
293
+
294
+ /* ── size="sm": o KPI de 18px/800 ──
295
+ 18px não está na escala de fonte do DS (16 e 20): é a medida dos KPIs da home do CrisDoc, mantida como literal.
296
+ Dois seletores de classe para vencer o 32/800 do `labelPosition="top"` sem depender da ordem. */
297
+ .umds-stat-card.umds-stat-card--sm .umds-stat-card__valor {
298
+ font-size: 1.125rem; /* 18px */
299
+ font-weight: var(--umds-primitive-font-weight-extrabold);
300
+ }
@@ -22,7 +22,11 @@ import './StatCard.css';
22
22
  * @property {string} trendIntent - Se a variação é boa ou ruim (cor), independente da direção: 'positive' | 'negative' | 'neutral'
23
23
  * @property {string} trendLabel - Texto da variação (ex.: '12% em relação ao mês anterior')
24
24
  * @property {string} description - Texto auxiliar abaixo do rótulo
25
+ * @property {string} size - 'md' (padrão) | 'sm': o `sm` leva o número a 18px/800, o tamanho dos KPIs da home do CrisDoc (fora da escala de fonte do DS, que vai de 16 a 20). Vale também com `labelPosition="top"`.
25
26
  * @property {string} labelPosition - Onde o rótulo aparece: 'bottom' (padrão, embaixo do número) | 'top' (no topo do cartão, acima do ícone e do número, com o número em 32/800). No HTML o número continua vindo antes do rótulo, então o leitor de tela anuncia igual.
27
+ * @property {string|number} secondaryValue - Segunda medida do cartão, menor, à direita da principal ("22,31%" do setor e "14,11%" do geral). Vem depois do resto no HTML. `0` aparece; vazio e ausente não.
28
+ * @property {string} secondaryLabel - O que a segunda medida significa ("Geral")
29
+ * @property {string} valueTone - Cor do número principal: 'default' (cor do texto) | 'primary' | 'positive' | 'negative' | 'info'. Usa as variantes escuras (contraste AA sobre o branco). Não confunda com `variant`, que pinta o ícone e a variação.
26
30
  * @property {string} infoTooltip - Texto de ajuda: mostra um ⓘ ao lado do rótulo com um Tooltip escuro. É um botão dentro do cartão; com `onClick` no cartão, evite (botão dentro de botão).
27
31
  * @property {string} infoLabel - Nome acessível do ⓘ. Troque para traduzir. Padrão: "Mais informações".
28
32
  * @property {string} align - Alinhamento do conteúdo
@@ -66,6 +70,14 @@ export interface StatCardProps {
66
70
  * @default 'bottom'
67
71
  */
68
72
  labelPosition?: "bottom" | "top";
73
+ /** Segunda medida, menor, à direita da principal. 0 aparece; vazio e ausente não. */
74
+ secondaryValue?: string | number;
75
+ /** O que a segunda medida significa. */
76
+ secondaryLabel?: string;
77
+ /** Tamanho do número: `sm` é o KPI de 18px/800. @default 'md' */
78
+ size?: "md" | "sm";
79
+ /** Cor do número principal. @default 'default' */
80
+ valueTone?: "default" | "primary" | "positive" | "negative" | "info";
69
81
  /**
70
82
  * Texto de ajuda: mostra o ⓘ ao lado do rótulo, com um Tooltip escuro. É um
71
83
  * botão focável dentro do cartão; com `onClick` no cartão, evite.
@@ -10,7 +10,12 @@ function StatCard(props) {
10
10
  "umds-stat-card",
11
11
  `umds-stat-card--${props.variant || "neutral"}`,
12
12
  props.align === "center" ? "umds-stat-card--centro" : "",
13
+ props.size === "sm" ? "umds-stat-card--sm" : "",
13
14
  props.labelPosition === "top" ? "umds-stat-card--rotulo-topo" : "",
15
+ temSecundaria() ? "umds-stat-card--com-secundaria" : "",
16
+ props.valueTone && props.valueTone !== "default"
17
+ ? "umds-stat-card--valor-" + props.valueTone
18
+ : "",
14
19
  props.onClick ? "umds-stat-card--acionavel" : "",
15
20
  props.className,
16
21
  ]
@@ -38,6 +43,11 @@ function StatCard(props) {
38
43
  return "↓";
39
44
  return "→";
40
45
  }
46
+ function temSecundaria() {
47
+ return (props.secondaryValue !== undefined &&
48
+ props.secondaryValue !== null &&
49
+ props.secondaryValue !== "");
50
+ }
41
51
  function ehAcionavel() {
42
52
  return !!props.onClick;
43
53
  }
@@ -48,6 +58,6 @@ function StatCard(props) {
48
58
  if (props.onClick)
49
59
  props.onClick();
50
60
  }
51
- return (_jsxs("div", { className: classes(), onClick: (event) => aoClicar(), role: ehAcionavel() ? "button" : undefined, tabIndex: ehAcionavel() ? 0 : undefined, children: [props.icon ? (_jsx("span", { className: "umds-stat-card__icone", "aria-hidden": "true", children: _jsx(Icon, { size: "md", variant: "inherit", icon: props.icon }) })) : null, _jsxs("div", { className: "umds-stat-card__corpo", children: [_jsx("span", { className: "umds-stat-card__valor", children: props.value }), _jsxs("span", { className: "umds-stat-card__rotulo", children: [props.label, props.infoTooltip ? (_jsx(Tooltip, { variant: "dark", side: "top", content: props.infoTooltip, children: _jsx("button", { type: "button", className: "umds-stat-card__info", "aria-label": props.infoLabel || "Mais informações", children: _jsx(Icon, { variant: "inherit", customSize: "14px", icon: faCircleInfo }) }) })) : null] }), props.description ? (_jsx("span", { className: "umds-stat-card__descricao", children: props.description })) : null, props.trendLabel ? (_jsxs("span", { className: classesDaVariacao(), children: [_jsx("span", { "aria-hidden": "true", children: simboloDaVariacao() }), props.trendLabel] })) : null] })] }));
61
+ return (_jsxs("div", { className: classes(), onClick: (event) => aoClicar(), role: ehAcionavel() ? "button" : undefined, tabIndex: ehAcionavel() ? 0 : undefined, children: [props.icon ? (_jsx("span", { className: "umds-stat-card__icone", "aria-hidden": "true", children: _jsx(Icon, { size: "md", variant: "inherit", icon: props.icon }) })) : null, _jsxs("div", { className: "umds-stat-card__corpo", children: [_jsx("span", { className: "umds-stat-card__valor", children: props.value }), _jsxs("span", { className: "umds-stat-card__rotulo", children: [props.label, props.infoTooltip ? (_jsx(Tooltip, { variant: "dark", side: "top", content: props.infoTooltip, children: _jsx("button", { type: "button", className: "umds-stat-card__info", "aria-label": props.infoLabel || "Mais informações", children: _jsx(Icon, { variant: "inherit", customSize: "14px", icon: faCircleInfo }) }) })) : null] }), props.description ? (_jsx("span", { className: "umds-stat-card__descricao", children: props.description })) : null, props.trendLabel ? (_jsxs("span", { className: classesDaVariacao(), children: [_jsx("span", { "aria-hidden": "true", children: simboloDaVariacao() }), props.trendLabel] })) : null] }), temSecundaria() ? (_jsxs("div", { className: "umds-stat-card__secundaria", children: [_jsx("span", { className: "umds-stat-card__valor-secundario", children: props.secondaryValue }), props.secondaryLabel ? (_jsx("span", { className: "umds-stat-card__rotulo-secundario", children: props.secondaryLabel })) : null] })) : null] }));
52
62
  }
53
63
  export default StatCard;
@@ -93,6 +93,17 @@
93
93
  white-space: nowrap;
94
94
  }
95
95
 
96
+ /* ============================
97
+ Densidade compacta (density="compact" no Tabs)
98
+ Altura mínima de 40px (o degrau sm do DS) e padding vertical de 8px: com o texto de 14px e a barra de
99
+ 3px, a aba fica nos 40. Só o padding vertical (padding-block), para não desfazer o espaço do × da aba
100
+ fechável. O contador em pílula não muda: cinza na aba inativa e azul só na ativa.
101
+ ============================ */
102
+ .umds-tabs--compact .umds-tabs__tab {
103
+ min-height: var(--umds-primitive-spacing-sm); /* 40px */
104
+ padding-block: var(--umds-primitive-spacing-nano);
105
+ }
106
+
96
107
  /* ============================
97
108
  Label
98
109
  ============================ */
@@ -14,6 +14,13 @@ export interface TabsProps {
14
14
  onChange?: (index: string) => void;
15
15
  /** Label de acessibilidade para o tablist */
16
16
  ariaLabel?: string;
17
+ /**
18
+ * Densidade das abas: `compact` leva a altura mínima de 48px a 40px (padding vertical de 8px), para barras
19
+ * de abas dentro de uma tela (o CrisDoc usa 40). O contador em pílula não muda: cinza na aba inativa e azul
20
+ * só na ativa, nas duas densidades.
21
+ * @default 'default'
22
+ */
23
+ density?: "default" | "compact";
17
24
  /**
18
25
  * Identificador estável usado como base dos ids acessíveis
19
26
  * (`{id}-tab-{index}` / `{id}-panel-{index}`). Passe o MESMO id no
@@ -6,7 +6,13 @@ import Icon from "../Icon/Icon";
6
6
  import TabsContext from "./tabs.context";
7
7
  function Tabs(props) {
8
8
  function classes() {
9
- return ["umds-tabs", props.className].filter(Boolean).join(" ");
9
+ return [
10
+ "umds-tabs",
11
+ props.density === "compact" ? "umds-tabs--compact" : "",
12
+ props.className,
13
+ ]
14
+ .filter(Boolean)
15
+ .join(" ");
10
16
  }
11
17
  function barClasses() {
12
18
  return ["umds-tabs__bar", props.onAdd ? "umds-tabs__bar--com-add" : ""]