@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
|
@@ -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
|
-
|
|
228
|
+
opened() ? "umds-select--open" : "",
|
|
171
229
|
props.searchVariant === "inline" ? "umds-select--search-inline" : "",
|
|
172
|
-
|
|
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 (!
|
|
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
|
|
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
|
-
|
|
221
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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: [!(
|
|
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:
|
|
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,
|
|
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 [
|
|
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" : ""]
|