@1doc/1ds-react 2.8.0 → 2.9.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 +1106 -0
- package/dist/components/AILabel/AILabel.js +1 -1
- package/dist/components/Accordion/Accordion.d.ts +6 -2
- package/dist/components/AttachmentCard/AttachmentCard.css +16 -0
- package/dist/components/AttachmentCard/AttachmentCard.d.ts +19 -2
- package/dist/components/AttachmentCard/AttachmentCard.js +40 -3
- package/dist/components/Avatar/Avatar.css +7 -2
- package/dist/components/Avatar/AvatarGroup.d.ts +17 -0
- package/dist/components/Avatar/AvatarGroup.js +22 -8
- package/dist/components/Badge/Badge.css +54 -2
- package/dist/components/Badge/Badge.d.ts +14 -4
- package/dist/components/Badge/Badge.js +1 -0
- package/dist/components/Box/Box.css +83 -0
- package/dist/components/Box/Box.d.ts +82 -2
- package/dist/components/Box/Box.js +42 -2
- package/dist/components/Button/Button.css +29 -0
- package/dist/components/Button/Button.d.ts +4 -4
- package/dist/components/Button/Button.js +4 -1
- package/dist/components/Calendar/Calendar.css +245 -0
- package/dist/components/Calendar/Calendar.d.ts +125 -0
- package/dist/components/Calendar/Calendar.js +320 -0
- package/dist/components/Chips/Chips.css +38 -0
- package/dist/components/Chips/Chips.d.ts +5 -2
- package/dist/components/Chips/Chips.js +2 -1
- package/dist/components/Coachmark/Coachmark.css +118 -0
- package/dist/components/Coachmark/Coachmark.d.ts +75 -0
- package/dist/components/Coachmark/Coachmark.js +321 -0
- package/dist/components/CodeBlock/CodeBlock.css +96 -0
- package/dist/components/CodeBlock/CodeBlock.d.ts +47 -0
- package/dist/components/CodeBlock/CodeBlock.js +103 -0
- package/dist/components/ColorPicker/ColorPicker.css +133 -0
- package/dist/components/ColorPicker/ColorPicker.d.ts +73 -0
- package/dist/components/ColorPicker/ColorPicker.js +219 -0
- package/dist/components/Combobox/Combobox.css +8 -0
- package/dist/components/DataTable/DataTable.css +80 -0
- package/dist/components/DataTable/DataTable.d.ts +19 -4
- package/dist/components/DataTable/DataTable.js +59 -15
- package/dist/components/DatePicker/DatePicker.css +8 -0
- package/dist/components/DatePicker/DatePicker.js +10 -1
- package/dist/components/DescriptionList/DescriptionList.css +113 -0
- package/dist/components/DescriptionList/DescriptionList.d.ts +59 -0
- package/dist/components/DescriptionList/DescriptionList.js +51 -0
- package/dist/components/FileInput/FileInput.css +16 -0
- package/dist/components/FileInput/FileInput.d.ts +4 -2
- package/dist/components/FileInput/FileInput.js +8 -2
- package/dist/components/Grid/Grid.js +5 -5
- package/dist/components/IconPicker/IconPicker.css +107 -0
- package/dist/components/IconPicker/IconPicker.d.ts +74 -0
- package/dist/components/IconPicker/IconPicker.js +194 -0
- package/dist/components/Input/Input.css +38 -0
- package/dist/components/Input/Input.d.ts +10 -1
- package/dist/components/Input/Input.js +66 -5
- package/dist/components/Kanban/Kanban.css +4 -0
- package/dist/components/Kbd/Kbd.css +66 -0
- package/dist/components/Kbd/Kbd.d.ts +32 -0
- package/dist/components/Kbd/Kbd.js +40 -0
- package/dist/components/List/List.css +136 -0
- package/dist/components/List/List.d.ts +82 -0
- package/dist/components/List/List.js +53 -0
- package/dist/components/List/ListItem.d.ts +85 -0
- package/dist/components/List/ListItem.js +81 -0
- package/dist/components/Modal/Modal.css +80 -0
- package/dist/components/Modal/Modal.d.ts +33 -6
- package/dist/components/Modal/Modal.js +101 -6
- package/dist/components/MultiSelect/MultiSelect.css +8 -0
- package/dist/components/MultiSelect/MultiSelect.js +10 -1
- package/dist/components/OrgChart/OrgChart.css +149 -0
- package/dist/components/OrgChart/OrgChart.d.ts +83 -0
- package/dist/components/OrgChart/OrgChart.js +297 -0
- package/dist/components/OtpInput/OtpInput.css +93 -0
- package/dist/components/OtpInput/OtpInput.d.ts +60 -0
- package/dist/components/OtpInput/OtpInput.js +215 -0
- package/dist/components/PhoneInput/PhoneInput.css +8 -0
- package/dist/components/Popover/Popover.d.ts +7 -0
- package/dist/components/Popover/Popover.js +11 -1
- package/dist/components/RichTextEditor/RichTextEditor.css +203 -0
- package/dist/components/RichTextEditor/RichTextEditor.d.ts +83 -0
- package/dist/components/RichTextEditor/RichTextEditor.js +494 -0
- package/dist/components/SegmentedControl/SegmentedControl.js +7 -2
- package/dist/components/Select/Select.css +70 -0
- package/dist/components/Select/Select.d.ts +12 -0
- package/dist/components/Select/Select.js +59 -4
- package/dist/components/Stack/Stack.css +49 -0
- package/dist/components/Stack/Stack.d.ts +65 -7
- package/dist/components/Stack/Stack.js +30 -2
- package/dist/components/StatCard/StatCard.css +73 -0
- package/dist/components/StatCard/StatCard.d.ts +17 -0
- package/dist/components/StatCard/StatCard.js +4 -1
- package/dist/components/Tabs/Tab.d.ts +15 -0
- package/dist/components/Tabs/Tab.js +30 -1
- package/dist/components/Tabs/Tabs.css +132 -0
- package/dist/components/Tabs/Tabs.d.ts +7 -0
- package/dist/components/Tabs/Tabs.js +13 -2
- package/dist/components/Tag/Tag.css +85 -2
- package/dist/components/Tag/Tag.d.ts +13 -6
- package/dist/components/Tag/Tag.js +1 -0
- package/dist/components/Text/Text.js +2 -2
- package/dist/components/Textarea/Textarea.css +8 -0
- package/dist/components/TimePicker/TimePicker.css +8 -0
- package/dist/components/Tooltip/Tooltip.css +25 -0
- package/dist/components/Tooltip/Tooltip.d.ts +11 -0
- package/dist/components/Tooltip/Tooltip.js +18 -3
- package/dist/index.d.ts +25 -0
- package/dist/index.js +14 -0
- package/dist/teste/AILabel.test.d.ts +1 -0
- package/dist/teste/AILabel.test.js +33 -0
- package/dist/teste/AccordionConteudoLivre.test.d.ts +1 -0
- package/dist/teste/AccordionConteudoLivre.test.js +29 -0
- package/dist/teste/AtributosRepassados.test.d.ts +1 -0
- package/dist/teste/AtributosRepassados.test.js +52 -0
- package/dist/teste/AttachmentCardIA.test.d.ts +1 -0
- package/dist/teste/AttachmentCardIA.test.js +89 -0
- package/dist/teste/AvatarGroupOverlap.test.d.ts +1 -0
- package/dist/teste/AvatarGroupOverlap.test.js +56 -0
- package/dist/teste/BadgeTag.test.d.ts +1 -0
- package/dist/teste/BadgeTag.test.js +42 -0
- package/dist/teste/BoxItemFlex.test.d.ts +1 -0
- package/dist/teste/BoxItemFlex.test.js +54 -0
- package/dist/teste/BoxLayout.test.d.ts +1 -0
- package/dist/teste/BoxLayout.test.js +35 -0
- package/dist/teste/Button.test.d.ts +1 -0
- package/dist/teste/Button.test.js +27 -0
- package/dist/teste/Calendar.test.d.ts +1 -0
- package/dist/teste/Calendar.test.js +221 -0
- package/dist/teste/ChipsXs.test.d.ts +1 -0
- package/dist/teste/ChipsXs.test.js +31 -0
- package/dist/teste/Coachmark.test.d.ts +1 -0
- package/dist/teste/Coachmark.test.js +149 -0
- package/dist/teste/ColorPicker.test.d.ts +1 -0
- package/dist/teste/ColorPicker.test.js +132 -0
- package/dist/teste/DataTableCelulaLivre.test.d.ts +1 -0
- package/dist/teste/DataTableCelulaLivre.test.js +76 -0
- package/dist/teste/DataTableDocumento.test.d.ts +1 -0
- package/dist/teste/DataTableDocumento.test.js +92 -0
- package/dist/teste/DatePicker.test.js +23 -0
- package/dist/teste/FileInputTrigger.test.d.ts +1 -0
- package/dist/teste/FileInputTrigger.test.js +53 -0
- package/dist/teste/IconPicker.test.d.ts +1 -0
- package/dist/teste/IconPicker.test.js +130 -0
- package/dist/teste/InputMascaraEAfins.test.d.ts +1 -0
- package/dist/teste/InputMascaraEAfins.test.js +99 -0
- package/dist/teste/KbdCodeBlock.test.d.ts +1 -0
- package/dist/teste/KbdCodeBlock.test.js +95 -0
- package/dist/teste/List.test.d.ts +1 -0
- package/dist/teste/List.test.js +153 -0
- package/dist/teste/ModalVariantes.test.d.ts +1 -0
- package/dist/teste/ModalVariantes.test.js +151 -0
- package/dist/teste/NomeAcessivelSsr.test.d.ts +1 -0
- package/dist/teste/NomeAcessivelSsr.test.js +88 -0
- package/dist/teste/OrgChart.test.d.ts +1 -0
- package/dist/teste/OrgChart.test.js +162 -0
- package/dist/teste/OtpInput.test.d.ts +1 -0
- package/dist/teste/OtpInput.test.js +143 -0
- package/dist/teste/PopoverMaxHeight.test.d.ts +1 -0
- package/dist/teste/PopoverMaxHeight.test.js +38 -0
- package/dist/teste/RichTextEditor.test.d.ts +1 -0
- package/dist/teste/RichTextEditor.test.js +287 -0
- package/dist/teste/SelectPlacementFiltro.test.d.ts +1 -0
- package/dist/teste/SelectPlacementFiltro.test.js +88 -0
- package/dist/teste/Stack.test.d.ts +1 -0
- package/dist/teste/Stack.test.js +57 -0
- package/dist/teste/StatCardRotuloTopo.test.d.ts +1 -0
- package/dist/teste/StatCardRotuloTopo.test.js +34 -0
- package/dist/teste/TabsContadorFechavel.test.d.ts +1 -0
- package/dist/teste/TabsContadorFechavel.test.js +79 -0
- package/dist/teste/TooltipDark.test.d.ts +1 -0
- package/dist/teste/TooltipDark.test.js +31 -0
- package/dist/utils/calendario.d.ts +28 -0
- package/dist/utils/calendario.js +73 -0
- package/dist/utils/html-seguro.d.ts +27 -0
- package/dist/utils/html-seguro.js +97 -0
- package/dist/utils/paleta-de-cores.d.ts +26 -0
- package/dist/utils/paleta-de-cores.js +10 -0
- package/dist/utils/texto.d.ts +5 -0
- package/dist/utils/texto.js +5 -0
- package/package.json +3 -3
- package/src/index.ts +26 -0
- package/src/teste/AILabel.test.tsx +41 -0
- package/src/teste/AccordionConteudoLivre.test.tsx +39 -0
- package/src/teste/AtributosRepassados.test.tsx +73 -0
- package/src/teste/AttachmentCardIA.test.tsx +126 -0
- package/src/teste/AvatarGroupOverlap.test.tsx +74 -0
- package/src/teste/BadgeTag.test.tsx +64 -0
- package/src/teste/BoxItemFlex.test.tsx +81 -0
- package/src/teste/BoxLayout.test.tsx +50 -0
- package/src/teste/Button.test.tsx +39 -0
- package/src/teste/Calendar.test.tsx +282 -0
- package/src/teste/ChipsXs.test.tsx +41 -0
- package/src/teste/Coachmark.test.tsx +198 -0
- package/src/teste/ColorPicker.test.tsx +153 -0
- package/src/teste/DataTableCelulaLivre.test.tsx +103 -0
- package/src/teste/DataTableDocumento.test.tsx +102 -0
- package/src/teste/DatePicker.test.tsx +38 -0
- package/src/teste/FileInputTrigger.test.tsx +89 -0
- package/src/teste/IconPicker.test.tsx +145 -0
- package/src/teste/InputMascaraEAfins.test.tsx +124 -0
- package/src/teste/KbdCodeBlock.test.tsx +106 -0
- package/src/teste/List.test.tsx +269 -0
- package/src/teste/ModalVariantes.test.tsx +173 -0
- package/src/teste/NomeAcessivelSsr.test.tsx +110 -0
- package/src/teste/OrgChart.test.tsx +183 -0
- package/src/teste/OtpInput.test.tsx +163 -0
- package/src/teste/PopoverMaxHeight.test.tsx +47 -0
- package/src/teste/RichTextEditor.test.tsx +329 -0
- package/src/teste/SelectPlacementFiltro.test.tsx +110 -0
- package/src/teste/Stack.test.tsx +80 -0
- package/src/teste/StatCardRotuloTopo.test.tsx +44 -0
- package/src/teste/TabsContadorFechavel.test.tsx +144 -0
- package/src/teste/TooltipDark.test.tsx +51 -0
- package/src/utils/calendario.ts +78 -0
- package/src/utils/html-seguro.ts +94 -0
- package/src/utils/paleta-de-cores.ts +34 -0
- package/src/utils/texto.ts +5 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import './RichTextEditor.css';
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { useState, useRef, useEffect } from "react";
|
|
6
|
+
import { faBold, faItalic, faUnderline, faStrikethrough, faListUl, faListOl, faLink, faRotateLeft, faRotateRight, faEraser, } from "@fortawesome/free-solid-svg-icons";
|
|
7
|
+
import Button from "../Button/Button";
|
|
8
|
+
import Icon from "../Icon/Icon";
|
|
9
|
+
import Input from "../Input/Input";
|
|
10
|
+
import Text from "../Text/Text";
|
|
11
|
+
import { gerarId } from "../../utils/id";
|
|
12
|
+
import { htmlDoLink, htmlVazio, normalizarEndereco, sanitizarHtml, } from "../../utils/html-seguro";
|
|
13
|
+
function RichTextEditor(props) {
|
|
14
|
+
const editorRef = useRef(null);
|
|
15
|
+
const toolbarRef = useRef(null);
|
|
16
|
+
const linkInputWrapRef = useRef(null);
|
|
17
|
+
const ultimoEmitidoRef = useRef("");
|
|
18
|
+
const selecaoRef = useRef(null);
|
|
19
|
+
const focoPendenteRef = useRef("");
|
|
20
|
+
const observadorRef = useRef(null);
|
|
21
|
+
const [uid, setUid] = useState(() => "");
|
|
22
|
+
const [linkOpen, setLinkOpen] = useState(() => false);
|
|
23
|
+
const [linkUrl, setLinkUrl] = useState(() => "");
|
|
24
|
+
const [linkError, setLinkError] = useState(() => false);
|
|
25
|
+
const [toolFocus, setToolFocus] = useState(() => 0);
|
|
26
|
+
const [active, setActive] = useState(() => "");
|
|
27
|
+
function baseId() {
|
|
28
|
+
return props.id || uid;
|
|
29
|
+
}
|
|
30
|
+
function labelId() {
|
|
31
|
+
return baseId() && props.label ? baseId() + "-label" : undefined;
|
|
32
|
+
}
|
|
33
|
+
function editorId() {
|
|
34
|
+
return baseId() ? baseId() + "-editor" : undefined;
|
|
35
|
+
}
|
|
36
|
+
function errorId() {
|
|
37
|
+
return baseId() && props.errorMessage ? baseId() + "-error" : undefined;
|
|
38
|
+
}
|
|
39
|
+
function helperId() {
|
|
40
|
+
return baseId() && props.helperText ? baseId() + "-helper" : undefined;
|
|
41
|
+
}
|
|
42
|
+
function describedBy() {
|
|
43
|
+
const ids = [errorId(), helperId()].filter(Boolean).join(" ");
|
|
44
|
+
return ids || undefined;
|
|
45
|
+
}
|
|
46
|
+
function editorAriaLabel() {
|
|
47
|
+
if (props.ariaLabel)
|
|
48
|
+
return props.ariaLabel;
|
|
49
|
+
return props.label && !labelId() ? props.label : undefined;
|
|
50
|
+
}
|
|
51
|
+
function isInvalid() {
|
|
52
|
+
return !!props.invalid || !!props.errorMessage;
|
|
53
|
+
}
|
|
54
|
+
function isEditable() {
|
|
55
|
+
return !props.disabled && !props.readOnly;
|
|
56
|
+
}
|
|
57
|
+
function editableText() {
|
|
58
|
+
return isEditable() ? "true" : "false";
|
|
59
|
+
}
|
|
60
|
+
function showToolbar() {
|
|
61
|
+
return !props.readOnly;
|
|
62
|
+
}
|
|
63
|
+
function classes() {
|
|
64
|
+
return [
|
|
65
|
+
"umds-rte",
|
|
66
|
+
isInvalid() ? "umds-rte--invalid" : "",
|
|
67
|
+
props.disabled ? "umds-rte--disabled" : "",
|
|
68
|
+
props.readOnly ? "umds-rte--readonly" : "",
|
|
69
|
+
props.resizable ? "umds-rte--resizable" : "",
|
|
70
|
+
props.className,
|
|
71
|
+
]
|
|
72
|
+
.filter(Boolean)
|
|
73
|
+
.join(" ");
|
|
74
|
+
}
|
|
75
|
+
function editorStyle() {
|
|
76
|
+
const altura = props.minHeight !== undefined && props.minHeight !== null
|
|
77
|
+
? props.minHeight
|
|
78
|
+
: 0;
|
|
79
|
+
return altura
|
|
80
|
+
? {
|
|
81
|
+
minHeight: altura + "px",
|
|
82
|
+
}
|
|
83
|
+
: undefined;
|
|
84
|
+
}
|
|
85
|
+
function rotulo(chave) {
|
|
86
|
+
const padrao = {
|
|
87
|
+
bold: "Negrito",
|
|
88
|
+
italic: "Itálico",
|
|
89
|
+
underline: "Sublinhado",
|
|
90
|
+
strike: "Tachado",
|
|
91
|
+
ul: "Lista com marcadores",
|
|
92
|
+
ol: "Lista numerada",
|
|
93
|
+
link: "Link",
|
|
94
|
+
undo: "Desfazer",
|
|
95
|
+
redo: "Refazer",
|
|
96
|
+
clear: "Limpar formatação",
|
|
97
|
+
linkAddress: "Endereço do link",
|
|
98
|
+
linkApply: "Aplicar",
|
|
99
|
+
linkRemove: "Remover link",
|
|
100
|
+
linkCancel: "Cancelar",
|
|
101
|
+
linkInvalid: "Use um endereço http, https, mailto ou tel.",
|
|
102
|
+
};
|
|
103
|
+
const dado = props.labels || {};
|
|
104
|
+
return dado[chave] || padrao[chave];
|
|
105
|
+
}
|
|
106
|
+
function toolbarName() {
|
|
107
|
+
return props.toolbarLabel || "Formatação";
|
|
108
|
+
}
|
|
109
|
+
function tools() {
|
|
110
|
+
const padrao = [
|
|
111
|
+
"bold",
|
|
112
|
+
"italic",
|
|
113
|
+
"underline",
|
|
114
|
+
"|",
|
|
115
|
+
"ul",
|
|
116
|
+
"ol",
|
|
117
|
+
"|",
|
|
118
|
+
"link",
|
|
119
|
+
"|",
|
|
120
|
+
"undo",
|
|
121
|
+
"redo",
|
|
122
|
+
];
|
|
123
|
+
const lista = props.toolbar && props.toolbar.length ? props.toolbar : padrao;
|
|
124
|
+
const catalogo = {
|
|
125
|
+
bold: {
|
|
126
|
+
command: "bold",
|
|
127
|
+
icon: faBold,
|
|
128
|
+
toggle: true,
|
|
129
|
+
},
|
|
130
|
+
italic: {
|
|
131
|
+
command: "italic",
|
|
132
|
+
icon: faItalic,
|
|
133
|
+
toggle: true,
|
|
134
|
+
},
|
|
135
|
+
underline: {
|
|
136
|
+
command: "underline",
|
|
137
|
+
icon: faUnderline,
|
|
138
|
+
toggle: true,
|
|
139
|
+
},
|
|
140
|
+
strike: {
|
|
141
|
+
command: "strikeThrough",
|
|
142
|
+
icon: faStrikethrough,
|
|
143
|
+
toggle: true,
|
|
144
|
+
},
|
|
145
|
+
ul: {
|
|
146
|
+
command: "insertUnorderedList",
|
|
147
|
+
icon: faListUl,
|
|
148
|
+
toggle: true,
|
|
149
|
+
},
|
|
150
|
+
ol: {
|
|
151
|
+
command: "insertOrderedList",
|
|
152
|
+
icon: faListOl,
|
|
153
|
+
toggle: true,
|
|
154
|
+
},
|
|
155
|
+
link: {
|
|
156
|
+
command: "link",
|
|
157
|
+
icon: faLink,
|
|
158
|
+
toggle: false,
|
|
159
|
+
},
|
|
160
|
+
undo: {
|
|
161
|
+
command: "undo",
|
|
162
|
+
icon: faRotateLeft,
|
|
163
|
+
toggle: false,
|
|
164
|
+
},
|
|
165
|
+
redo: {
|
|
166
|
+
command: "redo",
|
|
167
|
+
icon: faRotateRight,
|
|
168
|
+
toggle: false,
|
|
169
|
+
},
|
|
170
|
+
clear: {
|
|
171
|
+
command: "removeFormat",
|
|
172
|
+
icon: faEraser,
|
|
173
|
+
toggle: false,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
const saida = [];
|
|
177
|
+
let posicao = 0;
|
|
178
|
+
for (let i = 0; i < lista.length; i++) {
|
|
179
|
+
const chave = lista[i];
|
|
180
|
+
if (chave === "|") {
|
|
181
|
+
saida.push({
|
|
182
|
+
key: "sep-" + i,
|
|
183
|
+
separator: true,
|
|
184
|
+
});
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const def = catalogo[chave];
|
|
188
|
+
if (!def)
|
|
189
|
+
continue;
|
|
190
|
+
saida.push({
|
|
191
|
+
key: chave,
|
|
192
|
+
separator: false,
|
|
193
|
+
command: def.command,
|
|
194
|
+
icon: def.icon,
|
|
195
|
+
toggle: def.toggle,
|
|
196
|
+
position: posicao,
|
|
197
|
+
label: rotulo(chave),
|
|
198
|
+
});
|
|
199
|
+
posicao = posicao + 1;
|
|
200
|
+
}
|
|
201
|
+
return saida;
|
|
202
|
+
}
|
|
203
|
+
function estaAtiva(ferramenta) {
|
|
204
|
+
return (!!ferramenta.toggle &&
|
|
205
|
+
active.split(",").indexOf(ferramenta.command) !== -1);
|
|
206
|
+
}
|
|
207
|
+
function pressedText(ferramenta) {
|
|
208
|
+
return ferramenta.toggle
|
|
209
|
+
? estaAtiva(ferramenta)
|
|
210
|
+
? "true"
|
|
211
|
+
: "false"
|
|
212
|
+
: undefined;
|
|
213
|
+
}
|
|
214
|
+
function toolClasses(ferramenta) {
|
|
215
|
+
return [
|
|
216
|
+
"umds-rte__tool",
|
|
217
|
+
estaAtiva(ferramenta) ? "umds-rte__tool--active" : "",
|
|
218
|
+
]
|
|
219
|
+
.filter(Boolean)
|
|
220
|
+
.join(" ");
|
|
221
|
+
}
|
|
222
|
+
function toolTabIndex(ferramenta) {
|
|
223
|
+
return ferramenta.position === toolFocus ? 0 : -1;
|
|
224
|
+
}
|
|
225
|
+
function escreverHtml(html) {
|
|
226
|
+
if (!editorRef.current)
|
|
227
|
+
return;
|
|
228
|
+
const limpo = sanitizarHtml(html || "");
|
|
229
|
+
if (editorRef.current.innerHTML !== limpo)
|
|
230
|
+
editorRef.current.innerHTML = limpo;
|
|
231
|
+
}
|
|
232
|
+
function selecaoNaArea() {
|
|
233
|
+
if (typeof window === "undefined" ||
|
|
234
|
+
!window.getSelection ||
|
|
235
|
+
!editorRef.current)
|
|
236
|
+
return false;
|
|
237
|
+
const selecao = window.getSelection();
|
|
238
|
+
return (!!selecao &&
|
|
239
|
+
selecao.rangeCount > 0 &&
|
|
240
|
+
editorRef.current.contains(selecao.anchorNode));
|
|
241
|
+
}
|
|
242
|
+
function lerEstados() {
|
|
243
|
+
if (typeof document === "undefined" ||
|
|
244
|
+
typeof document.queryCommandState !== "function")
|
|
245
|
+
return;
|
|
246
|
+
if (!selecaoNaArea())
|
|
247
|
+
return;
|
|
248
|
+
const comandos = [
|
|
249
|
+
"bold",
|
|
250
|
+
"italic",
|
|
251
|
+
"underline",
|
|
252
|
+
"strikeThrough",
|
|
253
|
+
"insertUnorderedList",
|
|
254
|
+
"insertOrderedList",
|
|
255
|
+
];
|
|
256
|
+
const ligados = [];
|
|
257
|
+
for (let i = 0; i < comandos.length; i++) {
|
|
258
|
+
try {
|
|
259
|
+
if (document.queryCommandState(comandos[i]))
|
|
260
|
+
ligados.push(comandos[i]);
|
|
261
|
+
}
|
|
262
|
+
catch (erro) {
|
|
263
|
+
// O navegador não conhece o comando: fica desligado.
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const texto = ligados.join(",");
|
|
267
|
+
if (texto !== active)
|
|
268
|
+
setActive(texto);
|
|
269
|
+
}
|
|
270
|
+
function emitir() {
|
|
271
|
+
if (!editorRef.current)
|
|
272
|
+
return;
|
|
273
|
+
let html = editorRef.current.innerHTML;
|
|
274
|
+
// Apagou tudo: o navegador deixa um <br> e o placeholder (que é :empty) não volta.
|
|
275
|
+
if (htmlVazio(html)) {
|
|
276
|
+
html = "";
|
|
277
|
+
if (editorRef.current.innerHTML !== "" &&
|
|
278
|
+
editorRef.current.querySelector("li") === null)
|
|
279
|
+
editorRef.current.innerHTML = "";
|
|
280
|
+
}
|
|
281
|
+
ultimoEmitidoRef.current = html;
|
|
282
|
+
if (props.onChange)
|
|
283
|
+
props.onChange(html);
|
|
284
|
+
}
|
|
285
|
+
function handleInput() {
|
|
286
|
+
emitir();
|
|
287
|
+
lerEstados();
|
|
288
|
+
}
|
|
289
|
+
function handlePaste(event) {
|
|
290
|
+
if (!event || !event.clipboardData)
|
|
291
|
+
return;
|
|
292
|
+
// Colar entra como texto puro: sem formatação de outro site e sem HTML estranho.
|
|
293
|
+
event.preventDefault();
|
|
294
|
+
const texto = event.clipboardData.getData("text/plain");
|
|
295
|
+
if (texto && typeof document !== "undefined")
|
|
296
|
+
document.execCommand("insertText", false, texto);
|
|
297
|
+
}
|
|
298
|
+
function handleEditorKeyDown(event) {
|
|
299
|
+
if (!event)
|
|
300
|
+
return;
|
|
301
|
+
if ((event.ctrlKey || event.metaKey) &&
|
|
302
|
+
!event.altKey &&
|
|
303
|
+
(event.key === "k" || event.key === "K")) {
|
|
304
|
+
event.preventDefault();
|
|
305
|
+
abrirLink();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function handleBlur(event) {
|
|
309
|
+
if (props.onBlur)
|
|
310
|
+
props.onBlur(event);
|
|
311
|
+
}
|
|
312
|
+
function handleToolMouseDown(event) {
|
|
313
|
+
if (event && event.preventDefault)
|
|
314
|
+
event.preventDefault();
|
|
315
|
+
}
|
|
316
|
+
function handleToolClick(ferramenta) {
|
|
317
|
+
if (!isEditable() || typeof document === "undefined")
|
|
318
|
+
return;
|
|
319
|
+
if (ferramenta.command === "link") {
|
|
320
|
+
abrirLink();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (editorRef.current && !selecaoNaArea())
|
|
324
|
+
editorRef.current.focus();
|
|
325
|
+
document.execCommand(ferramenta.command, false, undefined);
|
|
326
|
+
handleInput();
|
|
327
|
+
}
|
|
328
|
+
function handleToolFocus(ferramenta) {
|
|
329
|
+
if (toolFocus !== ferramenta.position)
|
|
330
|
+
setToolFocus(ferramenta.position);
|
|
331
|
+
}
|
|
332
|
+
function handleToolbarKeyDown(event) {
|
|
333
|
+
if (!event || !toolbarRef.current)
|
|
334
|
+
return;
|
|
335
|
+
const tecla = event.key;
|
|
336
|
+
if (tecla !== "ArrowRight" &&
|
|
337
|
+
tecla !== "ArrowLeft" &&
|
|
338
|
+
tecla !== "Home" &&
|
|
339
|
+
tecla !== "End")
|
|
340
|
+
return;
|
|
341
|
+
const botoes = toolbarRef.current.querySelectorAll("button");
|
|
342
|
+
const total = botoes.length;
|
|
343
|
+
if (!total)
|
|
344
|
+
return;
|
|
345
|
+
let atual = toolFocus;
|
|
346
|
+
if (tecla === "ArrowRight")
|
|
347
|
+
atual = (atual + 1) % total;
|
|
348
|
+
else if (tecla === "ArrowLeft")
|
|
349
|
+
atual = (atual - 1 + total) % total;
|
|
350
|
+
else if (tecla === "Home")
|
|
351
|
+
atual = 0;
|
|
352
|
+
else
|
|
353
|
+
atual = total - 1;
|
|
354
|
+
event.preventDefault();
|
|
355
|
+
setToolFocus(atual);
|
|
356
|
+
botoes[atual].focus();
|
|
357
|
+
}
|
|
358
|
+
function abrirLink() {
|
|
359
|
+
if (!isEditable() || typeof window === "undefined" || linkOpen)
|
|
360
|
+
return;
|
|
361
|
+
let enderecoAtual = "";
|
|
362
|
+
selecaoRef.current = null;
|
|
363
|
+
if (selecaoNaArea()) {
|
|
364
|
+
const selecao = window.getSelection();
|
|
365
|
+
selecaoRef.current = selecao.getRangeAt(0).cloneRange();
|
|
366
|
+
const no = selecao.anchorNode;
|
|
367
|
+
const elemento = no && no.nodeType === 3 ? no.parentNode : no;
|
|
368
|
+
const ancora = elemento && elemento.closest ? elemento.closest("a") : null;
|
|
369
|
+
if (ancora)
|
|
370
|
+
enderecoAtual = ancora.getAttribute("href") || "";
|
|
371
|
+
}
|
|
372
|
+
setLinkUrl(enderecoAtual);
|
|
373
|
+
setLinkError(false);
|
|
374
|
+
setLinkOpen(true);
|
|
375
|
+
focoPendenteRef.current = "link";
|
|
376
|
+
}
|
|
377
|
+
function fecharLink() {
|
|
378
|
+
setLinkOpen(false);
|
|
379
|
+
setLinkError(false);
|
|
380
|
+
focoPendenteRef.current = "editor";
|
|
381
|
+
}
|
|
382
|
+
function restaurarSelecao() {
|
|
383
|
+
if (typeof window === "undefined" || !window.getSelection)
|
|
384
|
+
return;
|
|
385
|
+
if (editorRef.current)
|
|
386
|
+
editorRef.current.focus();
|
|
387
|
+
if (selecaoRef.current) {
|
|
388
|
+
const selecao = window.getSelection();
|
|
389
|
+
selecao.removeAllRanges();
|
|
390
|
+
selecao.addRange(selecaoRef.current);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function handleLinkInput(texto) {
|
|
394
|
+
setLinkUrl(texto);
|
|
395
|
+
if (linkError)
|
|
396
|
+
setLinkError(false);
|
|
397
|
+
}
|
|
398
|
+
function aplicarLink() {
|
|
399
|
+
if (typeof document === "undefined")
|
|
400
|
+
return;
|
|
401
|
+
const endereco = normalizarEndereco(linkUrl);
|
|
402
|
+
if (!endereco) {
|
|
403
|
+
setLinkError(true);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
restaurarSelecao();
|
|
407
|
+
const selecao = window.getSelection();
|
|
408
|
+
const semTexto = !!selecao && selecao.isCollapsed;
|
|
409
|
+
// Sem texto escolhido, o próprio endereço vira o texto do link.
|
|
410
|
+
const comando = semTexto ? "insertHTML" : "createLink";
|
|
411
|
+
const argumento = semTexto ? htmlDoLink(endereco) : endereco;
|
|
412
|
+
document.execCommand(comando, false, argumento);
|
|
413
|
+
setLinkOpen(false);
|
|
414
|
+
focoPendenteRef.current = "editor";
|
|
415
|
+
handleInput();
|
|
416
|
+
}
|
|
417
|
+
function removerLink() {
|
|
418
|
+
if (typeof document === "undefined")
|
|
419
|
+
return;
|
|
420
|
+
restaurarSelecao();
|
|
421
|
+
document.execCommand("unlink", false, undefined);
|
|
422
|
+
setLinkOpen(false);
|
|
423
|
+
focoPendenteRef.current = "editor";
|
|
424
|
+
handleInput();
|
|
425
|
+
}
|
|
426
|
+
function handleLinkSubmit(event) {
|
|
427
|
+
if (event && event.preventDefault)
|
|
428
|
+
event.preventDefault();
|
|
429
|
+
aplicarLink();
|
|
430
|
+
}
|
|
431
|
+
function handleLinkKeyDown(event) {
|
|
432
|
+
if (event && event.key === "Escape") {
|
|
433
|
+
event.preventDefault();
|
|
434
|
+
event.stopPropagation();
|
|
435
|
+
fecharLink();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function linkErrorText() {
|
|
439
|
+
return linkError ? rotulo("linkInvalid") : undefined;
|
|
440
|
+
}
|
|
441
|
+
function linkRemoveVisible() {
|
|
442
|
+
return linkUrl !== "";
|
|
443
|
+
}
|
|
444
|
+
useEffect(() => {
|
|
445
|
+
setUid(gerarId("umds-rte"));
|
|
446
|
+
const inicial = props.value !== undefined ? props.value : props.defaultValue || "";
|
|
447
|
+
escreverHtml(inicial);
|
|
448
|
+
ultimoEmitidoRef.current = htmlVazio(inicial)
|
|
449
|
+
? ""
|
|
450
|
+
: editorRef.current
|
|
451
|
+
? editorRef.current.innerHTML
|
|
452
|
+
: "";
|
|
453
|
+
if (typeof document !== "undefined") {
|
|
454
|
+
const aoMudarSelecao = () => lerEstados();
|
|
455
|
+
document.addEventListener("selectionchange", aoMudarSelecao);
|
|
456
|
+
observadorRef.current = () => document.removeEventListener("selectionchange", aoMudarSelecao);
|
|
457
|
+
}
|
|
458
|
+
}, []);
|
|
459
|
+
useEffect(() => {
|
|
460
|
+
// `value` de fora diferente do que a área tem: reescreve (reset do formulário, valor vindo do servidor).
|
|
461
|
+
if (props.value !== undefined &&
|
|
462
|
+
editorRef.current &&
|
|
463
|
+
props.value !== ultimoEmitidoRef.current) {
|
|
464
|
+
const limpo = sanitizarHtml(props.value);
|
|
465
|
+
if (editorRef.current.innerHTML !== limpo &&
|
|
466
|
+
!(htmlVazio(limpo) && htmlVazio(editorRef.current.innerHTML))) {
|
|
467
|
+
editorRef.current.innerHTML = limpo;
|
|
468
|
+
}
|
|
469
|
+
ultimoEmitidoRef.current = props.value;
|
|
470
|
+
}
|
|
471
|
+
if (focoPendenteRef.current) {
|
|
472
|
+
const alvo = focoPendenteRef.current;
|
|
473
|
+
focoPendenteRef.current = "";
|
|
474
|
+
if (alvo === "editor" && editorRef.current) {
|
|
475
|
+
restaurarSelecao();
|
|
476
|
+
}
|
|
477
|
+
else if (alvo === "link" && linkInputWrapRef.current) {
|
|
478
|
+
const campo = linkInputWrapRef.current.querySelector("input");
|
|
479
|
+
if (campo)
|
|
480
|
+
campo.focus();
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
useEffect(() => {
|
|
485
|
+
return () => {
|
|
486
|
+
if (observadorRef.current) {
|
|
487
|
+
observadorRef.current();
|
|
488
|
+
observadorRef.current = null;
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
}, []);
|
|
492
|
+
return (_jsxs("div", { className: classes(), children: [props.label ? (_jsx("span", { className: "umds-rte__label", id: labelId(), children: props.label })) : null, _jsxs("div", { className: "umds-rte__frame", children: [showToolbar() ? (_jsx("div", { role: "toolbar", className: "umds-rte__toolbar", ref: toolbarRef, "aria-label": toolbarName(), "aria-controls": editorId(), onKeyDown: (event) => handleToolbarKeyDown(event), children: tools()?.map((ferramenta) => (_jsxs(React.Fragment, { children: [ferramenta.separator ? (_jsx("span", { role: "separator", "aria-orientation": "vertical", className: "umds-rte__separator" })) : null, !ferramenta.separator ? (_jsx("button", { type: "button", "aria-label": ferramenta.label, title: ferramenta.label, "aria-pressed": pressedText(ferramenta), tabIndex: toolTabIndex(ferramenta), disabled: !isEditable(), onMouseDown: (event) => handleToolMouseDown(event), onClick: (event) => handleToolClick(ferramenta), onFocus: (event) => handleToolFocus(ferramenta), className: toolClasses(ferramenta), children: _jsx(Icon, { size: "sm", variant: "inherit", icon: ferramenta.icon }) })) : null] }, ferramenta.key))) })) : null, linkOpen ? (_jsxs("form", { className: "umds-rte__link", noValidate: true, onSubmit: (event) => handleLinkSubmit(event), onKeyDown: (event) => handleLinkKeyDown(event), children: [_jsx("div", { className: "umds-rte__link-field", ref: linkInputWrapRef, children: _jsx(Input, { size: "sm", type: "url", placeholder: "https://", label: rotulo("linkAddress"), value: linkUrl, errorMessage: linkErrorText(), onChange: (texto) => handleLinkInput(texto) }) }), _jsxs("div", { className: "umds-rte__link-actions", children: [_jsx(Button, { variant: "primary", size: "sm", type: "submit", children: rotulo("linkApply") }), linkRemoveVisible() ? (_jsx(Button, { variant: "secondary", size: "sm", onClick: (event) => removerLink(), children: rotulo("linkRemove") })) : null, _jsx(Button, { variant: "text", size: "sm", onClick: (event) => fecharLink(), children: rotulo("linkCancel") })] })] })) : null, _jsx("div", { role: "textbox", "aria-multiline": "true", className: "umds-rte__editor", ref: editorRef, id: editorId(), "aria-label": editorAriaLabel(), "aria-labelledby": labelId(), "aria-describedby": describedBy(), "aria-invalid": isInvalid() ? "true" : undefined, "aria-readonly": props.readOnly ? "true" : undefined, "aria-disabled": props.disabled ? "true" : undefined, "aria-placeholder": props.placeholder, "data-placeholder": props.placeholder, contentEditable: editableText(), tabIndex: props.disabled ? -1 : 0, style: editorStyle(), onInput: (event) => handleInput(), onPaste: (event) => handlePaste(event), onKeyDown: (event) => handleEditorKeyDown(event), onBlur: (event) => handleBlur(event) }), props.footer ? (_jsx("div", { className: "umds-rte__footer", children: _jsx(_Fragment, { children: props.footer }) })) : null] }), props.errorMessage ? (_jsx(Text, { as: "span", variant: "caption", color: "negative", id: errorId(), children: props.errorMessage })) : null, props.helperText ? (_jsx(Text, { as: "span", variant: "caption", color: "secondary", id: helperId(), children: props.helperText })) : null] }));
|
|
493
|
+
}
|
|
494
|
+
export default RichTextEditor;
|
|
@@ -33,6 +33,9 @@ function SegmentedControl(props) {
|
|
|
33
33
|
function labelledByText() {
|
|
34
34
|
return props.label ? labelId() : undefined;
|
|
35
35
|
}
|
|
36
|
+
function groupAriaLabel() {
|
|
37
|
+
return props.label && !baseId() ? props.label : undefined;
|
|
38
|
+
}
|
|
36
39
|
function describedByText() {
|
|
37
40
|
return props.errorMessage ? errorId() : undefined;
|
|
38
41
|
}
|
|
@@ -66,8 +69,10 @@ function SegmentedControl(props) {
|
|
|
66
69
|
function rotuloDoSegmento(option) {
|
|
67
70
|
// Com `label` visivel, o `ariaLabel` e IGNORADO de proposito: nome
|
|
68
71
|
// acessivel diferente do texto visivel quebra o WCAG 2.5.3.
|
|
72
|
+
// Sem id (antes do onMount) o label-for nao liga a nada: o nome e o
|
|
73
|
+
// proprio texto visivel, o que tambem respeita o 2.5.3.
|
|
69
74
|
if (option.label)
|
|
70
|
-
return undefined;
|
|
75
|
+
return baseId() ? undefined : option.label;
|
|
71
76
|
// Ultimo recurso. `value` e nome ruim, mas radio anonimo e pior — e o
|
|
72
77
|
// TypeScript nao pega, porque `label` e `ariaLabel` sao os dois opcionais.
|
|
73
78
|
return option.ariaLabel || option.value;
|
|
@@ -83,6 +88,6 @@ function SegmentedControl(props) {
|
|
|
83
88
|
if (!props.id)
|
|
84
89
|
setUid(gerarId("umds-segmented"));
|
|
85
90
|
}, []);
|
|
86
|
-
return (_jsxs("div", { className: groupClasses(), children: [props.label ? (_jsxs("span", { className: "umds-segmented__group-label", id: labelId(), children: [props.label, props.required ? (_jsx("span", { className: "umds-segmented__required", children: " *" })) : null] })) : null, _jsx("div", { role: "radiogroup", className: "umds-segmented__track", "aria-labelledby": labelledByText(), "aria-describedby": describedByText(), "aria-invalid": ariaInvalidText(), "aria-required": ariaRequiredText(), children: optionList()?.map((option, index) => (_jsxs(React.Fragment, { children: [_jsx("input", { type: "radio", className: "umds-segmented__input", id: inputId(index), name: groupName(), value: option.value, checked: estaEscolhido(option), disabled: estaDesabilitado(option), "aria-label": rotuloDoSegmento(option), onChange: (event) => handleChange(option) }), _jsx("label", { className: "umds-segmented__item", htmlFor: inputId(index), children: _jsxs("span", { className: "umds-segmented__conteudo", children: [option.icon ? (_jsx(Icon, { size: "sm", variant: "inherit", icon: option.icon })) : null, option.label ? (_jsx("span", { className: "umds-segmented__texto", children: option.label })) : null] }) })] }, option.value))) }), props.errorMessage ? (_jsx(Text, { as: "span", variant: "caption", color: "negative", className: "umds-segmented__error-message", id: errorId(), children: props.errorMessage })) : null] }));
|
|
91
|
+
return (_jsxs("div", { className: groupClasses(), children: [props.label ? (_jsxs("span", { className: "umds-segmented__group-label", id: labelId(), children: [props.label, props.required ? (_jsx("span", { className: "umds-segmented__required", children: " *" })) : null] })) : null, _jsx("div", { role: "radiogroup", className: "umds-segmented__track", "aria-labelledby": labelledByText(), "aria-label": groupAriaLabel(), "aria-describedby": describedByText(), "aria-invalid": ariaInvalidText(), "aria-required": ariaRequiredText(), children: optionList()?.map((option, index) => (_jsxs(React.Fragment, { children: [_jsx("input", { type: "radio", className: "umds-segmented__input", id: inputId(index), name: groupName(), value: option.value, checked: estaEscolhido(option), disabled: estaDesabilitado(option), "aria-label": rotuloDoSegmento(option), onChange: (event) => handleChange(option) }), _jsx("label", { className: "umds-segmented__item", htmlFor: inputId(index), children: _jsxs("span", { className: "umds-segmented__conteudo", children: [option.icon ? (_jsx(Icon, { size: "sm", variant: "inherit", icon: option.icon })) : null, option.label ? (_jsx("span", { className: "umds-segmented__texto", children: option.label })) : null] }) })] }, option.value))) }), props.errorMessage ? (_jsx(Text, { as: "span", variant: "caption", color: "negative", className: "umds-segmented__error-message", id: errorId(), children: props.errorMessage })) : null] }));
|
|
87
92
|
}
|
|
88
93
|
export default SegmentedControl;
|
|
@@ -157,6 +157,68 @@
|
|
|
157
157
|
overflow: hidden;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
/* ── Lista que abre para cima (placement top, ou auto sem espaço embaixo) ── */
|
|
161
|
+
.umds-select--placement-top .umds-select__dropdown {
|
|
162
|
+
top: auto;
|
|
163
|
+
bottom: calc(100% + 4px);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* ── Rodapé da lista (slot footer): ações fixas abaixo das opções ── */
|
|
167
|
+
.umds-select__footer {
|
|
168
|
+
flex-shrink: 0;
|
|
169
|
+
display: flex;
|
|
170
|
+
align-items: center;
|
|
171
|
+
padding: var(--umds-primitive-spacing-nano) var(--umds-primitive-spacing-xxxs);
|
|
172
|
+
border-top: 1px solid var(--umds-semantic-color-neutral-light-medium);
|
|
173
|
+
background-color: var(--umds-semantic-color-background-elevated);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* ── Variante filter: botão de filtro de barra de ferramentas ──
|
|
177
|
+
Contornado em azul, largura do conteúdo, lista mais larga que o botão. */
|
|
178
|
+
.umds-select--filter {
|
|
179
|
+
width: auto;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.umds-select--filter.umds-select--full-width {
|
|
183
|
+
width: 100%;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.umds-select--filter .umds-select__container {
|
|
187
|
+
width: auto;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.umds-select--filter.umds-select--full-width .umds-select__container {
|
|
191
|
+
width: 100%;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.umds-select--filter .umds-select__trigger {
|
|
195
|
+
width: auto;
|
|
196
|
+
gap: var(--umds-primitive-spacing-nano);
|
|
197
|
+
border-color: var(--umds-semantic-color-primary-pure);
|
|
198
|
+
background-color: var(--umds-semantic-color-primary-light);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.umds-select--filter.umds-select--full-width .umds-select__trigger {
|
|
202
|
+
width: 100%;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.umds-select--filter .umds-select__trigger:hover:not(.umds-select--disabled .umds-select__trigger) {
|
|
206
|
+
border-color: var(--umds-semantic-color-primary-dark);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.umds-select--filter .umds-select__trigger .umds-text,
|
|
210
|
+
.umds-select--filter .umds-select__actions {
|
|
211
|
+
color: var(--umds-semantic-color-primary-pure);
|
|
212
|
+
font-weight: var(--umds-primitive-font-weight-semibold);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.umds-select--filter .umds-select__dropdown {
|
|
216
|
+
/* 240px: três vezes o degrau xxl (80px). Sem token de largura de menu. */
|
|
217
|
+
width: max-content;
|
|
218
|
+
min-width: max(100%, calc(var(--umds-primitive-spacing-xxl) * 3));
|
|
219
|
+
max-width: calc(100vw - var(--umds-primitive-spacing-xxxs) * 2);
|
|
220
|
+
}
|
|
221
|
+
|
|
160
222
|
/* ── Busca no dropdown ── */
|
|
161
223
|
.umds-select__search-container {
|
|
162
224
|
display: flex;
|
|
@@ -382,6 +444,14 @@
|
|
|
382
444
|
border-color: var(--umds-semantic-field-border-default);
|
|
383
445
|
}
|
|
384
446
|
|
|
447
|
+
/* Somente leitura não é desabilitado: o texto segue lido e a WCAG 1.4.3 vale
|
|
448
|
+
sem isenção. O placeholder padrão foi calibrado para fundo branco e cai para
|
|
449
|
+
~4,2:1 sobre o fundo de somente leitura; neutral-dark-down passa 4,5:1 em
|
|
450
|
+
todos os temas (1doc 6,78:1 · 1doc-dark 10,83:1 · alto contraste 16,48:1). */
|
|
451
|
+
.umds-select--readonly .umds-select__placeholder {
|
|
452
|
+
color: var(--umds-semantic-color-neutral-dark-down);
|
|
453
|
+
}
|
|
454
|
+
|
|
385
455
|
/* O campo de busca precisa de indicador de foco — o painel foca ele ao abrir.
|
|
386
456
|
Um outline no <input> desenhava um retângulo azul solto no meio do painel,
|
|
387
457
|
porque o input não tem borda própria para o anel acompanhar. O indicador vai
|
|
@@ -40,6 +40,10 @@ export interface SelectOption {
|
|
|
40
40
|
* @property {string} ariaLabel - Nome acessível quando não há label visível.
|
|
41
41
|
* @property {string} id - ID base dos elementos internos. Gerado automaticamente se omitido.
|
|
42
42
|
* @property {function} onChange - Callback com o(s) valor(es) selecionado(s).
|
|
43
|
+
* @property {string} placement - Onde a lista abre: 'bottom' (padrão) | 'top' | 'auto'. O `auto` abre para cima quando falta espaço embaixo da tela e há mais em cima (lista dentro do rodapé de um modal).
|
|
44
|
+
* @property {string} variant - 'default' | 'filter'. O `filter` é o botão de filtro de barra de ferramentas: contornado em azul, largura do conteúdo, lista mais larga que o botão, busca focada ao abrir. Combine com `searchable`, `emptyOptionLabel` e o slot `footer`.
|
|
45
|
+
* @property {string} emptyOptionLabel - Opção "sem filtro" no topo da lista (seleção única): marcada quando nada está escolhido; ao escolher, `onChange` recebe `""` e o campo volta a mostrar o `placeholder` (o nome do filtro).
|
|
46
|
+
* @property {any} footer - Rodapé fixo da lista, com ações ("Gerenciar marcadores"). Passe o elemento correspondente (React node ou Vue slot `footer`).
|
|
43
47
|
* @quandoUsar Escolher UM valor de uma lista fechada. O valor selecionado fica visível no campo.
|
|
44
48
|
* @quandoNaoUsar Para escolher VÁRIOS valores — use `MultiSelect`. Para um menu de AÇÕES (editar, excluir, duplicar) em vez de valores, use `Dropdown`. Para 2 a 4 opções mutuamente exclusivas sempre visíveis, use `Radio`.
|
|
45
49
|
*/
|
|
@@ -77,6 +81,14 @@ export interface SelectProps {
|
|
|
77
81
|
errorMessage?: string;
|
|
78
82
|
fullWidth?: boolean;
|
|
79
83
|
size?: "sm" | "md" | "lg";
|
|
84
|
+
/** Onde a lista abre. @default 'bottom' */
|
|
85
|
+
placement?: "bottom" | "top" | "auto";
|
|
86
|
+
/** Variante visual. @default 'default' */
|
|
87
|
+
variant?: "default" | "filter";
|
|
88
|
+
/** Opção "sem filtro" no topo da lista (seleção única). Escolhê-la chama `onChange("")`. */
|
|
89
|
+
emptyOptionLabel?: string;
|
|
90
|
+
/** Rodapé fixo da lista (ações). React node ou slot `footer` no Vue. */
|
|
91
|
+
footer?: any;
|
|
80
92
|
pagination?: boolean;
|
|
81
93
|
hasMore?: boolean;
|
|
82
94
|
onLoadMore?: () => void;
|