@govbr-ds/webcomponents 2.0.0-next.41 → 2.0.0-next.43
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/dist/cjs/br-breadcrumb.cjs.entry.js +1 -1
- package/dist/cjs/br-breadcrumb.cjs.entry.js.map +1 -1
- package/dist/cjs/br-breadcrumb.entry.cjs.js.map +1 -1
- package/dist/cjs/br-button.br-icon.entry.cjs.js.map +1 -1
- package/dist/cjs/br-button_2.cjs.entry.js +22 -3
- package/dist/cjs/br-button_2.cjs.entry.js.map +1 -1
- package/dist/cjs/br-collapse.br-list.entry.cjs.js.map +1 -1
- package/dist/cjs/br-collapse_2.cjs.entry.js +2 -2
- package/dist/cjs/br-collapse_2.cjs.entry.js.map +1 -1
- package/dist/cjs/br-dropdown.cjs.entry.js +324 -10
- package/dist/cjs/br-dropdown.cjs.entry.js.map +1 -1
- package/dist/cjs/br-dropdown.entry.cjs.js.map +1 -1
- package/dist/cjs/br-footer.cjs.entry.js +1 -1
- package/dist/cjs/br-footer.cjs.entry.js.map +1 -1
- package/dist/cjs/br-footer.entry.cjs.js.map +1 -1
- package/dist/cjs/br-item.cjs.entry.js +1 -1
- package/dist/cjs/br-item.cjs.entry.js.map +1 -1
- package/dist/cjs/br-item.entry.cjs.js.map +1 -1
- package/dist/cjs/br-scrim.cjs.entry.js +1 -1
- package/dist/cjs/br-scrim.cjs.entry.js.map +1 -1
- package/dist/cjs/br-scrim.entry.cjs.js.map +1 -1
- package/dist/cjs/br-tooltip.cjs.entry.js +2187 -0
- package/dist/cjs/br-tooltip.cjs.entry.js.map +1 -0
- package/dist/cjs/br-tooltip.entry.cjs.js.map +1 -0
- package/dist/cjs/br-upload.cjs.entry.js +1 -1
- package/dist/cjs/index-DAOr34NM.js +4 -0
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/webcomponents.cjs.js +1 -1
- package/dist/collection/collection-manifest.json +1 -0
- package/dist/collection/components/breadcrumb/breadcrumb.css +1 -1
- package/dist/collection/components/button/button.js +2 -1
- package/dist/collection/components/button/button.js.map +1 -1
- package/dist/collection/components/dropdown/dropdown.css +1 -1
- package/dist/collection/components/dropdown/dropdown.js +346 -9
- package/dist/collection/components/dropdown/dropdown.js.map +1 -1
- package/dist/collection/components/footer/footer.css +1 -1
- package/dist/collection/components/icon/icon.css +1 -1
- package/dist/collection/components/icon/icon.js +40 -1
- package/dist/collection/components/icon/icon.js.map +1 -1
- package/dist/collection/components/item/item.css +1 -1
- package/dist/collection/components/list/list.css +1 -1
- package/dist/collection/components/list/list.js +1 -1
- package/dist/collection/components/list/list.js.map +1 -1
- package/dist/collection/components/scrim/scrim.css +1 -1
- package/dist/collection/components/step-item/step-item.js +1 -1
- package/dist/collection/components/tooltip/tooltip.css +1 -0
- package/dist/collection/components/tooltip/tooltip.js +683 -0
- package/dist/collection/components/tooltip/tooltip.js.map +1 -0
- package/dist/collection/components/upload/upload.js +1 -1
- package/dist/collection/pages/components/tooltip/index.js +18 -0
- package/dist/collection/pages/scripts/menu.js +14 -1
- package/dist/collection/utils/floating-ui.js +119 -0
- package/dist/collection/utils/floating-ui.js.map +1 -0
- package/dist/components/br-avatar.js +1 -1
- package/dist/components/br-breadcrumb.js +6 -6
- package/dist/components/br-button.js +3 -3
- package/dist/components/br-collapse.js +4 -4
- package/dist/components/br-dropdown.js +332 -25
- package/dist/components/br-dropdown.js.map +1 -1
- package/dist/components/br-footer-category.js +3 -3
- package/dist/components/br-footer-item.js +1 -1
- package/dist/components/br-footer-social.js +2 -2
- package/dist/components/br-footer.js +1 -1
- package/dist/components/br-icon.js +4 -4
- package/dist/components/br-input.js +4 -4
- package/dist/components/br-item.js +4 -4
- package/dist/components/br-list.js +1 -1
- package/dist/components/br-message.js +2 -2
- package/dist/components/br-scrim.js +1 -1
- package/dist/components/br-scrim.js.map +1 -1
- package/dist/components/br-select.js +5 -5
- package/dist/components/br-step-item.js +2 -2
- package/dist/components/br-tab-item.js +1 -1
- package/dist/components/br-tag.js +7 -7
- package/dist/components/br-tooltip.d.ts +11 -0
- package/dist/components/br-tooltip.js +2144 -0
- package/dist/components/br-tooltip.js.map +1 -0
- package/dist/components/br-upload.js +11 -11
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +2 -0
- package/dist/components/{p-pq3SZemh.js → p-CIINlcIu.js} +3 -3
- package/dist/components/{p-brOPUYUL.js → p-DJtFpYiN.js} +3 -3
- package/dist/components/p-DJtFpYiN.js.map +1 -0
- package/dist/components/{p-DlMJ2wZW.js → p-DLL8l_-5.js} +7 -7
- package/dist/components/{p-DlMJ2wZW.js.map → p-DLL8l_-5.js.map} +1 -1
- package/dist/components/{p-BxU9TOn1.js → p-DuPFr2fm.js} +4 -4
- package/dist/components/{p-COPEUBKR.js → p-Dxc2AzjS.js} +6 -5
- package/dist/components/p-Dxc2AzjS.js.map +1 -0
- package/dist/components/{p-jx7aLpDh.js → p-vi3ehOfu.js} +40 -23
- package/dist/components/p-vi3ehOfu.js.map +1 -0
- package/dist/custom-docs/markdown-methods.js +2 -0
- package/dist/custom-docs/markdown-methods.js.map +1 -1
- package/dist/custom-elements.json +745 -76
- package/dist/esm/br-breadcrumb.entry.js +1 -1
- package/dist/esm/br-breadcrumb.entry.js.map +1 -1
- package/dist/esm/br-button.br-icon.entry.js.map +1 -1
- package/dist/esm/br-button_2.entry.js +22 -3
- package/dist/esm/br-button_2.entry.js.map +1 -1
- package/dist/esm/br-collapse.br-list.entry.js.map +1 -1
- package/dist/esm/br-collapse_2.entry.js +2 -2
- package/dist/esm/br-collapse_2.entry.js.map +1 -1
- package/dist/esm/br-dropdown.entry.js +324 -10
- package/dist/esm/br-dropdown.entry.js.map +1 -1
- package/dist/esm/br-footer.entry.js +1 -1
- package/dist/esm/br-footer.entry.js.map +1 -1
- package/dist/esm/br-item.entry.js +1 -1
- package/dist/esm/br-item.entry.js.map +1 -1
- package/dist/esm/br-scrim.entry.js +1 -1
- package/dist/esm/br-scrim.entry.js.map +1 -1
- package/dist/esm/br-tooltip.entry.js +2185 -0
- package/dist/esm/br-tooltip.entry.js.map +1 -0
- package/dist/esm/br-upload.entry.js +1 -1
- package/dist/esm/index-ClsF4De3.js +4 -0
- package/dist/esm/loader.js +1 -1
- package/dist/esm/webcomponents.js +1 -1
- package/dist/hydrate/index.js +2558 -23
- package/dist/hydrate/index.mjs +2558 -23
- package/dist/types/components/button/button.d.ts +1 -0
- package/dist/types/components/dropdown/dropdown.d.ts +48 -1
- package/dist/types/components/icon/icon.d.ts +7 -0
- package/dist/types/components/tooltip/tooltip.d.ts +238 -0
- package/dist/types/components.d.ts +139 -0
- package/dist/types/utils/floating-ui.d.ts +78 -0
- package/dist/webcomponents/br-breadcrumb.entry.esm.js.map +1 -1
- package/dist/webcomponents/br-button.br-icon.entry.esm.js.map +1 -1
- package/dist/webcomponents/br-collapse.br-list.entry.esm.js.map +1 -1
- package/dist/webcomponents/br-dropdown.entry.esm.js.map +1 -1
- package/dist/webcomponents/br-footer.entry.esm.js.map +1 -1
- package/dist/webcomponents/br-item.entry.esm.js.map +1 -1
- package/dist/webcomponents/br-scrim.entry.esm.js.map +1 -1
- package/dist/webcomponents/br-tooltip.entry.esm.js.map +1 -0
- package/dist/webcomponents/{p-c3267481.entry.js → p-3d5600cb.entry.js} +2 -2
- package/dist/webcomponents/p-3d5600cb.entry.js.map +1 -0
- package/dist/webcomponents/{p-713035b5.entry.js → p-5c638bff.entry.js} +4 -4
- package/dist/webcomponents/{p-713035b5.entry.js.map → p-5c638bff.entry.js.map} +1 -1
- package/dist/webcomponents/{p-5a36e6be.entry.js → p-83fd1103.entry.js} +2 -2
- package/dist/webcomponents/{p-ab251560.entry.js → p-8ac9dcff.entry.js} +329 -23
- package/dist/webcomponents/p-8ac9dcff.entry.js.map +1 -0
- package/dist/webcomponents/{p-dbfc2a44.entry.js → p-c25d500c.entry.js} +2 -2
- package/dist/webcomponents/p-c25d500c.entry.js.map +1 -0
- package/dist/webcomponents/{p-383a4240.entry.js → p-d01d51a9.entry.js} +27 -10
- package/dist/webcomponents/p-d01d51a9.entry.js.map +1 -0
- package/dist/webcomponents/{p-1cf7cc9c.entry.js → p-d1bc7827.entry.js} +2 -2
- package/dist/webcomponents/p-e15ccd94.entry.js +2098 -0
- package/dist/webcomponents/p-e15ccd94.entry.js.map +1 -0
- package/dist/webcomponents/{p-ad8edd59.entry.js → p-f3ab9e06.entry.js} +10 -10
- package/dist/webcomponents/webcomponents.esm.js +24 -10
- package/dist/webcomponents.html-custom-data.json +18 -2
- package/package.json +1 -1
- package/dist/components/p-COPEUBKR.js.map +0 -1
- package/dist/components/p-brOPUYUL.js.map +0 -1
- package/dist/components/p-jx7aLpDh.js.map +0 -1
- package/dist/webcomponents/p-383a4240.entry.js.map +0 -1
- package/dist/webcomponents/p-ab251560.entry.js.map +0 -1
- package/dist/webcomponents/p-c3267481.entry.js.map +0 -1
- package/dist/webcomponents/p-dbfc2a44.entry.js.map +0 -1
- /package/dist/components/{p-pq3SZemh.js.map → p-CIINlcIu.js.map} +0 -0
- /package/dist/components/{p-BxU9TOn1.js.map → p-DuPFr2fm.js.map} +0 -0
- /package/dist/webcomponents/{p-5a36e6be.entry.js.map → p-83fd1103.entry.js.map} +0 -0
- /package/dist/webcomponents/{p-1cf7cc9c.entry.js.map → p-d1bc7827.entry.js.map} +0 -0
- /package/dist/webcomponents/{p-ad8edd59.entry.js.map → p-f3ab9e06.entry.js.map} +0 -0
|
@@ -27,23 +27,278 @@ export class Dropdown {
|
|
|
27
27
|
* Caso não seja fornecido, um ID gerado automaticamente será usado.
|
|
28
28
|
*/
|
|
29
29
|
customId = `br-dropdown-${dropdownId++}`;
|
|
30
|
+
// Elementos DOM privados
|
|
31
|
+
triggerElement;
|
|
32
|
+
targetElement = null;
|
|
33
|
+
dropdownItems = [];
|
|
34
|
+
dropdownLinks = [];
|
|
35
|
+
focusableElement = null;
|
|
36
|
+
// Event handlers
|
|
37
|
+
clickHandler;
|
|
38
|
+
triggerKeydownHandler;
|
|
39
|
+
targetKeydownHandler;
|
|
30
40
|
/**
|
|
31
41
|
* Método chamado sempre que a propriedade `isOpen` muda.
|
|
32
42
|
* Emite um evento com o novo estado do dropdown.
|
|
33
43
|
*/
|
|
34
|
-
isOpenChanged() {
|
|
35
|
-
this.
|
|
44
|
+
isOpenChanged(newValue) {
|
|
45
|
+
if (this.triggerElement !== null && this.triggerElement !== undefined) {
|
|
46
|
+
if (newValue) {
|
|
47
|
+
this.triggerElement.setAttribute('aria-expanded', 'true');
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
this.triggerElement.removeAttribute('aria-expanded');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (newValue) {
|
|
54
|
+
this.initializeDropdownItems();
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
if (this.triggerElement !== null && this.triggerElement !== undefined) {
|
|
58
|
+
this.focusOnTriggerElement();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
this.brDropdownChange.emit({ 'is-opened': newValue });
|
|
36
62
|
}
|
|
37
63
|
connectedCallback() {
|
|
38
|
-
|
|
64
|
+
this.clickHandler = this.onClickOutside.bind(this);
|
|
65
|
+
document.addEventListener('click', this.clickHandler);
|
|
39
66
|
}
|
|
40
67
|
disconnectedCallback() {
|
|
41
|
-
document.removeEventListener('click', this.
|
|
68
|
+
document.removeEventListener('click', this.clickHandler);
|
|
69
|
+
if (this.triggerElement !== null && this.triggerElement !== undefined) {
|
|
70
|
+
this.triggerElement.removeEventListener('keydown', this.triggerKeydownHandler);
|
|
71
|
+
}
|
|
72
|
+
if (this.targetElement !== null && this.targetElement !== undefined) {
|
|
73
|
+
this.targetElement.removeEventListener('keydown', this.targetKeydownHandler);
|
|
74
|
+
}
|
|
75
|
+
this.clearDropdownItemListeners();
|
|
76
|
+
}
|
|
77
|
+
clearDropdownItemListeners() {
|
|
78
|
+
this.dropdownItems.forEach((item) => {
|
|
79
|
+
item.removeEventListener('keydown', this.handleItemKeydown);
|
|
80
|
+
});
|
|
81
|
+
this.dropdownLinks.forEach((link) => {
|
|
82
|
+
link.removeEventListener('keydown', this.handleLinkKeydown);
|
|
83
|
+
});
|
|
84
|
+
this.dropdownItems = [];
|
|
85
|
+
this.dropdownLinks = [];
|
|
86
|
+
}
|
|
87
|
+
componentDidLoad() {
|
|
88
|
+
this.triggerElement = this.el.shadowRoot.querySelector('#trigger');
|
|
89
|
+
this.targetElement = this.el.shadowRoot.querySelector('#target');
|
|
90
|
+
this.setBehavior();
|
|
91
|
+
if (this.isOpen) {
|
|
92
|
+
this.initializeDropdownItems();
|
|
93
|
+
}
|
|
42
94
|
}
|
|
43
95
|
/**
|
|
44
96
|
* Evento emitido quando o estado do dropdown muda.
|
|
45
97
|
*/
|
|
46
98
|
brDropdownChange;
|
|
99
|
+
/**
|
|
100
|
+
* Configura os comportamentos de interação do dropdown
|
|
101
|
+
*/
|
|
102
|
+
setBehavior() {
|
|
103
|
+
if (this.triggerElement === null)
|
|
104
|
+
return;
|
|
105
|
+
this.triggerKeydownHandler = (event) => {
|
|
106
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
this.handleTriggerBehavior(event);
|
|
109
|
+
}
|
|
110
|
+
else if (event.key === 'Escape') {
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
this.hide();
|
|
113
|
+
this.focusOnTriggerElement();
|
|
114
|
+
}
|
|
115
|
+
else if (event.key === 'ArrowDown' && this.isOpen) {
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
this.initializeDropdownItems();
|
|
118
|
+
if (this.dropdownLinks.length > 0) {
|
|
119
|
+
this.focusOnElement(this.dropdownLinks[0]);
|
|
120
|
+
}
|
|
121
|
+
else if (this.dropdownItems.length > 0) {
|
|
122
|
+
this.focusOnElement(this.dropdownItems[0]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
this.targetKeydownHandler = (event) => {
|
|
127
|
+
if (event.key === 'Escape') {
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
this.hide();
|
|
130
|
+
this.focusOnTriggerElement();
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
this.triggerElement.addEventListener('keydown', this.triggerKeydownHandler);
|
|
134
|
+
if (this.targetElement) {
|
|
135
|
+
this.targetElement.addEventListener('keydown', this.targetKeydownHandler);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Lida com o comportamento no trigger
|
|
140
|
+
*/
|
|
141
|
+
handleTriggerBehavior(event) {
|
|
142
|
+
event.preventDefault();
|
|
143
|
+
this.toggle(event);
|
|
144
|
+
if (this.isOpen) {
|
|
145
|
+
setTimeout(() => {
|
|
146
|
+
this.initializeDropdownItems();
|
|
147
|
+
}, 0);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Inicializa os itens do dropdown com comportamentos de navegação com teclado,
|
|
152
|
+
* separando claramente entre elementos navegáveis por Tab e elementos navegáveis por setas
|
|
153
|
+
*/
|
|
154
|
+
initializeDropdownItems() {
|
|
155
|
+
if (this.targetElement === null)
|
|
156
|
+
return;
|
|
157
|
+
this.clearDropdownItemListeners();
|
|
158
|
+
this.targetElement.setAttribute('tabindex', '-1');
|
|
159
|
+
this.targetElement.focus();
|
|
160
|
+
const targetSlot = this.el.shadowRoot.querySelector('slot[name="target"]');
|
|
161
|
+
if (targetSlot === null || targetSlot === undefined)
|
|
162
|
+
return;
|
|
163
|
+
const assignedElements = targetSlot.assignedElements();
|
|
164
|
+
if (assignedElements.length === 0)
|
|
165
|
+
return;
|
|
166
|
+
const nonLinkSelector = 'button, br-button, input, br-input, br-item:not([href]), textarea, br-textarea, select, br-select, details, [tabindex]:not([tabindex="-1"]):not(a[href])';
|
|
167
|
+
const linkSelector = 'a[href], br-item[href]';
|
|
168
|
+
assignedElements.forEach((element) => {
|
|
169
|
+
if (element instanceof HTMLElement) {
|
|
170
|
+
if (element.matches(linkSelector)) {
|
|
171
|
+
this.dropdownLinks.push(element);
|
|
172
|
+
}
|
|
173
|
+
else if (element.matches(nonLinkSelector)) {
|
|
174
|
+
this.dropdownItems.push(element);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const linkChildren = Array.from(element.querySelectorAll(linkSelector));
|
|
178
|
+
this.dropdownLinks = this.dropdownLinks.concat(linkChildren);
|
|
179
|
+
const nonLinkChildren = Array.from(element.querySelectorAll(nonLinkSelector));
|
|
180
|
+
this.dropdownItems = this.dropdownItems.concat(nonLinkChildren);
|
|
181
|
+
});
|
|
182
|
+
this.dropdownItems.forEach((item) => {
|
|
183
|
+
item.addEventListener('keydown', this.handleItemKeydown);
|
|
184
|
+
});
|
|
185
|
+
this.dropdownLinks.forEach((link) => {
|
|
186
|
+
link.addEventListener('keydown', this.handleLinkKeydown);
|
|
187
|
+
});
|
|
188
|
+
if (this.dropdownLinks.length > 0) {
|
|
189
|
+
this.focusOnElement(this.dropdownLinks[0]);
|
|
190
|
+
this.focusableElement = this.dropdownLinks[0];
|
|
191
|
+
}
|
|
192
|
+
else if (this.dropdownItems.length > 0) {
|
|
193
|
+
this.focusOnElement(this.dropdownItems[0]);
|
|
194
|
+
this.focusableElement = this.dropdownItems[0];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Handler específico para links
|
|
199
|
+
*/
|
|
200
|
+
handleLinkKeydown = (event) => {
|
|
201
|
+
const link = event.currentTarget;
|
|
202
|
+
const key = event.key;
|
|
203
|
+
const linkIndex = this.dropdownLinks.indexOf(link);
|
|
204
|
+
const lastLinkIndex = this.dropdownLinks.length - 1;
|
|
205
|
+
switch (key) {
|
|
206
|
+
case 'ArrowUp':
|
|
207
|
+
case 'ArrowDown':
|
|
208
|
+
case 'Home':
|
|
209
|
+
case 'End':
|
|
210
|
+
event.preventDefault();
|
|
211
|
+
event.stopPropagation();
|
|
212
|
+
break;
|
|
213
|
+
case 'Tab':
|
|
214
|
+
event.preventDefault();
|
|
215
|
+
if (!event.shiftKey) {
|
|
216
|
+
if (linkIndex >= lastLinkIndex) {
|
|
217
|
+
this.focusOnElement(this.dropdownLinks[0]);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
this.focusOnElement(this.dropdownLinks[linkIndex + 1]);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
if (linkIndex <= 0) {
|
|
225
|
+
this.focusOnElement(this.dropdownLinks[lastLinkIndex]);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
this.focusOnElement(this.dropdownLinks[linkIndex - 1]);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
case 'Enter':
|
|
233
|
+
case ' ':
|
|
234
|
+
if (key === ' ' && link.tagName.toLowerCase() === 'a') {
|
|
235
|
+
event.preventDefault();
|
|
236
|
+
}
|
|
237
|
+
setTimeout(() => this.hide(), 100);
|
|
238
|
+
break;
|
|
239
|
+
case 'Escape':
|
|
240
|
+
event.preventDefault();
|
|
241
|
+
this.hide();
|
|
242
|
+
this.focusOnTriggerElement();
|
|
243
|
+
break;
|
|
244
|
+
default:
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
/**
|
|
249
|
+
* Handler para eventos de keydown nos itens não-link
|
|
250
|
+
* Implementa navegação completa por setas, Tab não navega entre estes itens
|
|
251
|
+
*/
|
|
252
|
+
handleItemKeydown = (event) => {
|
|
253
|
+
const item = event.currentTarget;
|
|
254
|
+
const key = event.key;
|
|
255
|
+
const currentIndex = this.dropdownItems.indexOf(item);
|
|
256
|
+
const lastIndex = this.dropdownItems.length - 1;
|
|
257
|
+
switch (key) {
|
|
258
|
+
case 'ArrowUp':
|
|
259
|
+
event.preventDefault();
|
|
260
|
+
if (currentIndex <= 0) {
|
|
261
|
+
this.focusOnElement(this.dropdownItems[lastIndex]);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
this.focusOnElement(this.dropdownItems[currentIndex - 1]);
|
|
265
|
+
}
|
|
266
|
+
break;
|
|
267
|
+
case 'ArrowDown':
|
|
268
|
+
event.preventDefault();
|
|
269
|
+
if (currentIndex >= lastIndex) {
|
|
270
|
+
this.focusOnElement(this.dropdownItems[0]);
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
this.focusOnElement(this.dropdownItems[currentIndex + 1]);
|
|
274
|
+
}
|
|
275
|
+
break;
|
|
276
|
+
case 'Home':
|
|
277
|
+
event.preventDefault();
|
|
278
|
+
this.focusOnElement(this.dropdownItems[0]);
|
|
279
|
+
break;
|
|
280
|
+
case 'End':
|
|
281
|
+
event.preventDefault();
|
|
282
|
+
this.focusOnElement(this.dropdownItems[lastIndex]);
|
|
283
|
+
break;
|
|
284
|
+
case 'Enter':
|
|
285
|
+
case ' ':
|
|
286
|
+
event.preventDefault();
|
|
287
|
+
item.click();
|
|
288
|
+
this.hide();
|
|
289
|
+
this.focusOnTriggerElement();
|
|
290
|
+
break;
|
|
291
|
+
case 'Tab':
|
|
292
|
+
break;
|
|
293
|
+
case 'Escape':
|
|
294
|
+
event.preventDefault();
|
|
295
|
+
this.hide();
|
|
296
|
+
this.focusOnTriggerElement();
|
|
297
|
+
break;
|
|
298
|
+
default:
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
47
302
|
getCssClassMap() {
|
|
48
303
|
return {
|
|
49
304
|
'br-dropdown': true,
|
|
@@ -62,9 +317,13 @@ export class Dropdown {
|
|
|
62
317
|
* @param {MouseEvent} event - O evento de clique que ocorreu.
|
|
63
318
|
*/
|
|
64
319
|
onClickOutside(event) {
|
|
320
|
+
if (!this.isOpen)
|
|
321
|
+
return;
|
|
65
322
|
const path = event.composedPath();
|
|
66
|
-
|
|
67
|
-
|
|
323
|
+
if (this.triggerElement &&
|
|
324
|
+
this.targetElement &&
|
|
325
|
+
!path.includes(this.triggerElement) &&
|
|
326
|
+
!path.includes(this.targetElement)) {
|
|
68
327
|
this.hide();
|
|
69
328
|
}
|
|
70
329
|
}
|
|
@@ -75,7 +334,7 @@ export class Dropdown {
|
|
|
75
334
|
*/
|
|
76
335
|
async open() {
|
|
77
336
|
this.isOpen = true;
|
|
78
|
-
return { isOpen: this.isOpen };
|
|
337
|
+
return { isOpen: this.isOpen };
|
|
79
338
|
}
|
|
80
339
|
/**
|
|
81
340
|
* Esconde o dropdown.
|
|
@@ -84,10 +343,71 @@ export class Dropdown {
|
|
|
84
343
|
*/
|
|
85
344
|
async hide() {
|
|
86
345
|
this.isOpen = false;
|
|
87
|
-
return { isOpen: this.isOpen };
|
|
346
|
+
return { isOpen: this.isOpen };
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Foca em um elemento específico com suporte a componentes personalizados
|
|
350
|
+
* @param targetElement Elemento a receber o foco
|
|
351
|
+
*/
|
|
352
|
+
focusOnElement(targetElement) {
|
|
353
|
+
if (targetElement && targetElement !== this.el) {
|
|
354
|
+
if ('setFocus' in targetElement) {
|
|
355
|
+
;
|
|
356
|
+
targetElement.setFocus();
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
targetElement.focus();
|
|
360
|
+
}
|
|
361
|
+
targetElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Foca no elemento interativo dentro do slot trigger
|
|
366
|
+
* Esta função procura pelo elemento dentro do slot trigger e aplica o foco nele
|
|
367
|
+
*/
|
|
368
|
+
focusOnTriggerElement() {
|
|
369
|
+
const triggerSlot = this.el.shadowRoot.querySelector('slot[name="trigger"]');
|
|
370
|
+
if (triggerSlot) {
|
|
371
|
+
const slotElements = triggerSlot.assignedElements();
|
|
372
|
+
if (slotElements.length > 0) {
|
|
373
|
+
const firstElement = slotElements[0];
|
|
374
|
+
if ('setFocus' in firstElement) {
|
|
375
|
+
;
|
|
376
|
+
firstElement.setFocus();
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
firstElement.focus();
|
|
380
|
+
}
|
|
381
|
+
firstElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
if (this.triggerElement) {
|
|
385
|
+
this.triggerElement.focus();
|
|
386
|
+
this.triggerElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
if (this.triggerElement) {
|
|
392
|
+
this.triggerElement.focus();
|
|
393
|
+
this.triggerElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Define o foco no elemento interno do componente.
|
|
399
|
+
* Este método pode ser chamado externamente para garantir que o foco seja aplicado ao elemento correto.
|
|
400
|
+
*/
|
|
401
|
+
async setFocus() {
|
|
402
|
+
if (this.focusableElement !== null && this.focusableElement !== undefined) {
|
|
403
|
+
this.focusableElement.focus();
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
this.focusOnTriggerElement();
|
|
407
|
+
}
|
|
88
408
|
}
|
|
89
409
|
render() {
|
|
90
|
-
return (h(Host, { key: '
|
|
410
|
+
return (h(Host, { key: '02cb03b288f7a36357cce0c3fd835ae67f357473', "is-open": this.isOpen }, h("div", { key: '7cd2f136a3c4479e56b0846c62379127524006c9', class: this.getCssClassMap(), id: this.customId }, h("div", { key: 'ced3927426f40e6768a242fb18c370c143b494fb', id: "trigger", onClick: this.toggle, "aria-controls": "target", "aria-haspopup": "true" }, h("slot", { key: '844d8f708daf611c70f36a293ab852305e4231ba', name: "trigger" })), h("div", { key: '835dd4b23f3a09faa9ced1bd5b0c3c05f8e7bc76', id: "target", "aria-hidden": !this.isOpen, role: "menu" }, h("slot", { key: 'e8286c83c4231ba7324ce322729bb290bc69fa96', name: "target" })))));
|
|
91
411
|
}
|
|
92
412
|
static get is() { return "br-dropdown"; }
|
|
93
413
|
static get encapsulation() { return "shadow"; }
|
|
@@ -205,6 +525,23 @@ export class Dropdown {
|
|
|
205
525
|
"text": "() para que possa ser chamado externamente."
|
|
206
526
|
}]
|
|
207
527
|
}
|
|
528
|
+
},
|
|
529
|
+
"setFocus": {
|
|
530
|
+
"complexType": {
|
|
531
|
+
"signature": "() => Promise<void>",
|
|
532
|
+
"parameters": [],
|
|
533
|
+
"references": {
|
|
534
|
+
"Promise": {
|
|
535
|
+
"location": "global",
|
|
536
|
+
"id": "global::Promise"
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
"return": "Promise<void>"
|
|
540
|
+
},
|
|
541
|
+
"docs": {
|
|
542
|
+
"text": "Define o foco no elemento interno do componente.\nEste m\u00E9todo pode ser chamado externamente para garantir que o foco seja aplicado ao elemento correto.",
|
|
543
|
+
"tags": []
|
|
544
|
+
}
|
|
208
545
|
}
|
|
209
546
|
};
|
|
210
547
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown.js","sourceRoot":"","sources":["../../../../../../packages/webcomponents/src/components/dropdown/dropdown.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAgB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAItH;;;;;GAKG;AAOH,MAAM,OAAO,QAAQ;IACnB;;;OAGG;IACQ,EAAE,CAAuB;IAEjB,gBAAgB,CAAkB;IAErD;;;;OAIG;IACqC,MAAM,GAAY,KAAK,CAAA;IAE/D;;;OAGG;IAC+B,QAAQ,GAAW,eAAe,UAAU,EAAE,EAAE,CAAA;IAElF;;;OAGG;IAEH,aAAa;QACX,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;IAC1D,CAAC;IAED,iBAAiB;QACf,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IACpE,CAAC;IAED,oBAAoB;QAClB,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IACvE,CAAC;IAED;;OAEG;IACM,gBAAgB,CAAwC;IAEzD,cAAc;QACpB,OAAO;YACL,aAAa,EAAE,IAAI;SACpB,CAAA;IACH,CAAC;IAED;;OAEG;IACc,MAAM,GAAG,CAAC,KAAY,EAAE,EAAE;QACzC,KAAK,CAAC,eAAe,EAAE,CAAA;QACvB,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAA;IAC5B,CAAC,CAAA;IAED;;;;OAIG;IACK,cAAc,CAAC,KAAiB;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,EAAE,CAAA;QACjC,MAAM,eAAe,GAAG,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAA;QAEnE,IAAI,eAAe,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;YACvD,IAAI,CAAC,IAAI,EAAE,CAAA;QACb,CAAC;IACH,CAAC;IAED;;;;OAIG;IAEH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;QAClB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAA,CAAC,wBAAwB;IACzD,CAAC;IAED;;;;OAIG;IAEH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,MAAM,GAAG,KAAK,CAAA;QACnB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAA,CAAC,wBAAwB;IACzD,CAAC;IAED,MAAM;QACJ,OAAO,CACL,EAAC,IAAI,gEAAU,IAAI,CAAC,MAAM;YACxB,4DAAK,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ;gBAClD,4DACE,EAAE,EAAC,SAAS,EACZ,OAAO,EAAE,IAAI,CAAC,MAAM,mBACN,QAAQ,mBACR,MAAM,mBACL,IAAI,CAAC,MAAM;oBAE1B,6DAAM,IAAI,EAAC,SAAS,GAAQ,CACxB;gBACN,4DAAK,EAAE,EAAC,QAAQ,iBAAc,CAAC,IAAI,CAAC,MAAM;oBACxC,6DAAM,IAAI,EAAC,QAAQ,GAAQ,CACvB,CACF,CACD,CACR,CAAA;IACH,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CACF;AAED,IAAI,UAAU,GAAG,CAAC,CAAA","sourcesContent":["import { AttachInternals, Component, Element, Event, EventEmitter, h, Host, Method, Prop, Watch } from '@stencil/core'\n\nimport { CssClassMap } from '../../utils/interfaces'\n\n/**\n * Para uma descrição detalhada, consulte a [documentação do GovBR-DS](https://www.gov.br/ds/components/dropdown?tab=designer).\n *\n * @slot trigger - Slot que recebe o elemento acionador\n * @slot target - Slot que recebe o elemento alvo\n */\n@Component({\n tag: 'br-dropdown',\n styleUrl: 'dropdown.scss',\n shadow: true,\n formAssociated: true,\n})\nexport class Dropdown {\n /**\n * Referência ao elemento host do componente.\n * Utilize esta propriedade para acessar e manipular o elemento do DOM associado ao componente.\n */\n @Element() el: HTMLBrDropdownElement\n\n @AttachInternals() elementInternals: ElementInternals\n\n /**\n * Indica se o dropdown está aberto ou fechado.\n * Esta propriedade é refletida no DOM e pode ser alterada externamente.\n * O valor padrão é falso (fechado).\n */\n @Prop({ reflect: true, mutable: true }) isOpen: boolean = false\n\n /**\n * Identificador único.\n * Caso não seja fornecido, um ID gerado automaticamente será usado.\n */\n @Prop({ reflect: true }) readonly customId: string = `br-dropdown-${dropdownId++}`\n\n /**\n * Método chamado sempre que a propriedade `isOpen` muda.\n * Emite um evento com o novo estado do dropdown.\n */\n @Watch('isOpen')\n isOpenChanged() {\n this.brDropdownChange.emit({ 'is-opened': this.isOpen })\n }\n\n connectedCallback() {\n document.addEventListener('click', this.onClickOutside.bind(this))\n }\n\n disconnectedCallback() {\n document.removeEventListener('click', this.onClickOutside.bind(this))\n }\n\n /**\n * Evento emitido quando o estado do dropdown muda.\n */\n @Event() brDropdownChange: EventEmitter<{ 'is-opened': boolean }>\n\n private getCssClassMap(): CssClassMap {\n return {\n 'br-dropdown': true,\n }\n }\n\n /**\n * Alterna o estado do dropdown entre aberto e fechado.\n */\n private readonly toggle = (event: Event) => {\n event.stopPropagation()\n this.isOpen = !this.isOpen\n }\n\n /**\n * Método que lida com cliques fora do dropdown.\n *\n * @param {MouseEvent} event - O evento de clique que ocorreu.\n */\n private onClickOutside(event: MouseEvent) {\n const path = event.composedPath()\n const dropdownElement = this.el.shadowRoot.querySelector('#target')\n\n if (dropdownElement && !path.includes(dropdownElement)) {\n this.hide()\n }\n }\n\n /**\n * Abre o dropdown.\n * Define a propriedade `isOpen` como verdadeira e retorna o novo estado.\n * Este método é marcado com @Method() para que possa ser chamado externamente.\n */\n @Method()\n async open(): Promise<{ isOpen: boolean }> {\n this.isOpen = true\n return { isOpen: this.isOpen } // Retorna o novo estado\n }\n\n /**\n * Esconde o dropdown.\n * Define a propriedade `isOpen` como falsa e retorna o novo estado.\n * Este método é marcado com @Method() para que possa ser chamado externamente.\n */\n @Method()\n async hide(): Promise<{ isOpen: boolean }> {\n this.isOpen = false\n return { isOpen: this.isOpen } // Retorna o novo estado\n }\n\n render() {\n return (\n <Host is-open={this.isOpen}>\n <div class={this.getCssClassMap()} id={this.customId}>\n <div\n id=\"trigger\"\n onClick={this.toggle}\n aria-controls=\"target\"\n aria-haspopup=\"true\"\n aria-expanded={this.isOpen}\n >\n <slot name=\"trigger\"></slot>\n </div>\n <div id=\"target\" aria-hidden={!this.isOpen}>\n <slot name=\"target\"></slot>\n </div>\n </div>\n </Host>\n )\n }\n}\n\nlet dropdownId = 0\n"]}
|
|
1
|
+
{"version":3,"file":"dropdown.js","sourceRoot":"","sources":["../../../../../../packages/webcomponents/src/components/dropdown/dropdown.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AttachInternals, Component, Element, Event, EventEmitter, h, Host, Method, Prop, Watch } from '@stencil/core'\n\nimport { CssClassMap } from '../../utils/interfaces'\n\n/**\n * Para uma descrição detalhada, consulte a [documentação do GovBR-DS](https://www.gov.br/ds/components/dropdown?tab=designer).\n *\n * @slot trigger - Slot que recebe o elemento acionador\n * @slot target - Slot que recebe o elemento alvo\n */\n@Component({\n tag: 'br-dropdown',\n styleUrl: 'dropdown.scss',\n shadow: true,\n formAssociated: true,\n})\nexport class Dropdown {\n /**\n * Referência ao elemento host do componente.\n * Utilize esta propriedade para acessar e manipular o elemento do DOM associado ao componente.\n */\n @Element() el: HTMLBrDropdownElement\n\n @AttachInternals() elementInternals: ElementInternals\n\n /**\n * Indica se o dropdown está aberto ou fechado.\n * Esta propriedade é refletida no DOM e pode ser alterada externamente.\n * O valor padrão é falso (fechado).\n */\n @Prop({ reflect: true, mutable: true }) isOpen: boolean = false\n\n /**\n * Identificador único.\n * Caso não seja fornecido, um ID gerado automaticamente será usado.\n */\n @Prop({ reflect: true }) readonly customId: string = `br-dropdown-${dropdownId++}`\n\n // Elementos DOM privados\n private triggerElement: HTMLElement\n private targetElement: HTMLElement | null = null\n private dropdownItems: HTMLElement[] = []\n private dropdownLinks: HTMLElement[] = []\n private focusableElement: HTMLElement | null | undefined = null\n\n // Event handlers\n private clickHandler: (event: MouseEvent) => void\n private triggerKeydownHandler: (event: KeyboardEvent) => void\n private targetKeydownHandler: (event: KeyboardEvent) => void\n\n /**\n * Método chamado sempre que a propriedade `isOpen` muda.\n * Emite um evento com o novo estado do dropdown.\n */\n @Watch('isOpen')\n isOpenChanged(newValue: boolean) {\n if (this.triggerElement !== null && this.triggerElement !== undefined) {\n if (newValue) {\n this.triggerElement.setAttribute('aria-expanded', 'true')\n } else {\n this.triggerElement.removeAttribute('aria-expanded')\n }\n }\n\n if (newValue) {\n this.initializeDropdownItems()\n } else {\n if (this.triggerElement !== null && this.triggerElement !== undefined) {\n this.focusOnTriggerElement()\n }\n }\n\n this.brDropdownChange.emit({ 'is-opened': newValue })\n }\n\n connectedCallback() {\n this.clickHandler = this.onClickOutside.bind(this)\n document.addEventListener('click', this.clickHandler)\n }\n\n disconnectedCallback() {\n document.removeEventListener('click', this.clickHandler)\n\n if (this.triggerElement !== null && this.triggerElement !== undefined) {\n this.triggerElement.removeEventListener('keydown', this.triggerKeydownHandler)\n }\n\n if (this.targetElement !== null && this.targetElement !== undefined) {\n this.targetElement.removeEventListener('keydown', this.targetKeydownHandler)\n }\n\n this.clearDropdownItemListeners()\n }\n\n private clearDropdownItemListeners() {\n this.dropdownItems.forEach((item) => {\n item.removeEventListener('keydown', this.handleItemKeydown)\n })\n\n this.dropdownLinks.forEach((link) => {\n link.removeEventListener('keydown', this.handleLinkKeydown)\n })\n\n this.dropdownItems = []\n this.dropdownLinks = []\n }\n\n componentDidLoad() {\n this.triggerElement = this.el.shadowRoot.querySelector('#trigger')\n this.targetElement = this.el.shadowRoot.querySelector('#target')\n\n this.setBehavior()\n\n if (this.isOpen) {\n this.initializeDropdownItems()\n }\n }\n\n /**\n * Evento emitido quando o estado do dropdown muda.\n */\n @Event() brDropdownChange: EventEmitter<{ 'is-opened': boolean }>\n\n /**\n * Configura os comportamentos de interação do dropdown\n */\n private setBehavior() {\n if (this.triggerElement === null) return\n\n this.triggerKeydownHandler = (event: KeyboardEvent) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n this.handleTriggerBehavior(event)\n } else if (event.key === 'Escape') {\n event.preventDefault()\n this.hide()\n this.focusOnTriggerElement()\n } else if (event.key === 'ArrowDown' && this.isOpen) {\n event.preventDefault()\n this.initializeDropdownItems()\n if (this.dropdownLinks.length > 0) {\n this.focusOnElement(this.dropdownLinks[0])\n } else if (this.dropdownItems.length > 0) {\n this.focusOnElement(this.dropdownItems[0])\n }\n }\n }\n\n this.targetKeydownHandler = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n this.hide()\n this.focusOnTriggerElement()\n }\n }\n\n this.triggerElement.addEventListener('keydown', this.triggerKeydownHandler)\n if (this.targetElement) {\n this.targetElement.addEventListener('keydown', this.targetKeydownHandler)\n }\n }\n\n /**\n * Lida com o comportamento no trigger\n */\n private handleTriggerBehavior(event: Event) {\n event.preventDefault()\n this.toggle(event)\n\n if (this.isOpen) {\n setTimeout(() => {\n this.initializeDropdownItems()\n }, 0)\n }\n }\n\n /**\n * Inicializa os itens do dropdown com comportamentos de navegação com teclado,\n * separando claramente entre elementos navegáveis por Tab e elementos navegáveis por setas\n */\n private initializeDropdownItems() {\n if (this.targetElement === null) return\n\n this.clearDropdownItemListeners()\n\n this.targetElement.setAttribute('tabindex', '-1')\n this.targetElement.focus()\n\n const targetSlot = this.el.shadowRoot.querySelector('slot[name=\"target\"]') as HTMLSlotElement\n if (targetSlot === null || targetSlot === undefined) return\n\n const assignedElements = targetSlot.assignedElements()\n if (assignedElements.length === 0) return\n\n const nonLinkSelector =\n 'button, br-button, input, br-input, br-item:not([href]), textarea, br-textarea, select, br-select, details, [tabindex]:not([tabindex=\"-1\"]):not(a[href])'\n\n const linkSelector = 'a[href], br-item[href]'\n\n assignedElements.forEach((element) => {\n if (element instanceof HTMLElement) {\n if (element.matches(linkSelector)) {\n this.dropdownLinks.push(element)\n } else if (element.matches(nonLinkSelector)) {\n this.dropdownItems.push(element)\n }\n }\n\n const linkChildren = Array.from(element.querySelectorAll(linkSelector)) as HTMLElement[]\n this.dropdownLinks = this.dropdownLinks.concat(linkChildren)\n\n const nonLinkChildren = Array.from(element.querySelectorAll(nonLinkSelector)) as HTMLElement[]\n this.dropdownItems = this.dropdownItems.concat(nonLinkChildren)\n })\n\n this.dropdownItems.forEach((item) => {\n item.addEventListener('keydown', this.handleItemKeydown)\n })\n\n this.dropdownLinks.forEach((link) => {\n link.addEventListener('keydown', this.handleLinkKeydown)\n })\n\n if (this.dropdownLinks.length > 0) {\n this.focusOnElement(this.dropdownLinks[0])\n this.focusableElement = this.dropdownLinks[0]\n } else if (this.dropdownItems.length > 0) {\n this.focusOnElement(this.dropdownItems[0])\n this.focusableElement = this.dropdownItems[0]\n }\n }\n\n /**\n * Handler específico para links\n */\n private handleLinkKeydown = (event: KeyboardEvent) => {\n const link = event.currentTarget as HTMLElement\n const key = event.key\n const linkIndex = this.dropdownLinks.indexOf(link)\n const lastLinkIndex = this.dropdownLinks.length - 1\n\n switch (key) {\n case 'ArrowUp':\n case 'ArrowDown':\n case 'Home':\n case 'End':\n event.preventDefault()\n event.stopPropagation()\n break\n case 'Tab':\n event.preventDefault()\n\n if (!event.shiftKey) {\n if (linkIndex >= lastLinkIndex) {\n this.focusOnElement(this.dropdownLinks[0])\n } else {\n this.focusOnElement(this.dropdownLinks[linkIndex + 1])\n }\n } else {\n if (linkIndex <= 0) {\n this.focusOnElement(this.dropdownLinks[lastLinkIndex])\n } else {\n this.focusOnElement(this.dropdownLinks[linkIndex - 1])\n }\n }\n break\n case 'Enter':\n case ' ':\n if (key === ' ' && link.tagName.toLowerCase() === 'a') {\n event.preventDefault()\n }\n setTimeout(() => this.hide(), 100)\n break\n case 'Escape':\n event.preventDefault()\n this.hide()\n this.focusOnTriggerElement()\n break\n default:\n break\n }\n }\n\n /**\n * Handler para eventos de keydown nos itens não-link\n * Implementa navegação completa por setas, Tab não navega entre estes itens\n */\n private handleItemKeydown = (event: KeyboardEvent) => {\n const item = event.currentTarget as HTMLElement\n const key = event.key\n const currentIndex = this.dropdownItems.indexOf(item)\n const lastIndex = this.dropdownItems.length - 1\n\n switch (key) {\n case 'ArrowUp':\n event.preventDefault()\n if (currentIndex <= 0) {\n this.focusOnElement(this.dropdownItems[lastIndex])\n } else {\n this.focusOnElement(this.dropdownItems[currentIndex - 1])\n }\n break\n\n case 'ArrowDown':\n event.preventDefault()\n if (currentIndex >= lastIndex) {\n this.focusOnElement(this.dropdownItems[0])\n } else {\n this.focusOnElement(this.dropdownItems[currentIndex + 1])\n }\n break\n\n case 'Home':\n event.preventDefault()\n this.focusOnElement(this.dropdownItems[0])\n break\n\n case 'End':\n event.preventDefault()\n this.focusOnElement(this.dropdownItems[lastIndex])\n break\n\n case 'Enter':\n case ' ':\n event.preventDefault()\n item.click()\n this.hide()\n this.focusOnTriggerElement()\n break\n\n case 'Tab':\n break\n\n case 'Escape':\n event.preventDefault()\n this.hide()\n this.focusOnTriggerElement()\n break\n\n default:\n break\n }\n }\n\n private getCssClassMap(): CssClassMap {\n return {\n 'br-dropdown': true,\n }\n }\n\n /**\n * Alterna o estado do dropdown entre aberto e fechado.\n */\n private readonly toggle = (event: Event) => {\n event.stopPropagation()\n this.isOpen = !this.isOpen\n }\n\n /**\n * Método que lida com cliques fora do dropdown.\n *\n * @param {MouseEvent} event - O evento de clique que ocorreu.\n */\n private onClickOutside(event: MouseEvent) {\n if (!this.isOpen) return\n\n const path = event.composedPath()\n\n if (\n this.triggerElement &&\n this.targetElement &&\n !path.includes(this.triggerElement) &&\n !path.includes(this.targetElement)\n ) {\n this.hide()\n }\n }\n\n /**\n * Abre o dropdown.\n * Define a propriedade `isOpen` como verdadeira e retorna o novo estado.\n * Este método é marcado com @Method() para que possa ser chamado externamente.\n */\n @Method()\n async open(): Promise<{ isOpen: boolean }> {\n this.isOpen = true\n return { isOpen: this.isOpen }\n }\n\n /**\n * Esconde o dropdown.\n * Define a propriedade `isOpen` como falsa e retorna o novo estado.\n * Este método é marcado com @Method() para que possa ser chamado externamente.\n */\n @Method()\n async hide(): Promise<{ isOpen: boolean }> {\n this.isOpen = false\n return { isOpen: this.isOpen }\n }\n\n /**\n * Foca em um elemento específico com suporte a componentes personalizados\n * @param targetElement Elemento a receber o foco\n */\n private focusOnElement(targetElement: HTMLElement) {\n if (targetElement && targetElement !== this.el) {\n if ('setFocus' in targetElement) {\n ;(targetElement as { setFocus: () => void }).setFocus()\n } else {\n targetElement.focus()\n }\n targetElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })\n }\n }\n\n /**\n * Foca no elemento interativo dentro do slot trigger\n * Esta função procura pelo elemento dentro do slot trigger e aplica o foco nele\n */\n private focusOnTriggerElement() {\n const triggerSlot = this.el.shadowRoot.querySelector('slot[name=\"trigger\"]') as HTMLSlotElement\n if (triggerSlot) {\n const slotElements = triggerSlot.assignedElements()\n if (slotElements.length > 0) {\n const firstElement = slotElements[0] as HTMLElement\n if ('setFocus' in firstElement) {\n ;(firstElement as { setFocus: () => void }).setFocus()\n } else {\n firstElement.focus()\n }\n firstElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })\n } else {\n if (this.triggerElement) {\n this.triggerElement.focus()\n this.triggerElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })\n }\n }\n } else {\n if (this.triggerElement) {\n this.triggerElement.focus()\n this.triggerElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })\n }\n }\n }\n\n /**\n * Define o foco no elemento interno do componente.\n * Este método pode ser chamado externamente para garantir que o foco seja aplicado ao elemento correto.\n */\n @Method()\n async setFocus() {\n if (this.focusableElement !== null && this.focusableElement !== undefined) {\n this.focusableElement.focus()\n } else {\n this.focusOnTriggerElement()\n }\n }\n\n render() {\n return (\n <Host is-open={this.isOpen}>\n <div class={this.getCssClassMap()} id={this.customId}>\n <div id=\"trigger\" onClick={this.toggle} aria-controls=\"target\" aria-haspopup=\"true\">\n <slot name=\"trigger\"></slot>\n </div>\n <div id=\"target\" aria-hidden={!this.isOpen} role=\"menu\">\n <slot name=\"target\"></slot>\n </div>\n </div>\n </Host>\n )\n }\n}\n\nlet dropdownId = 0\n"]}
|