|
1 | 1 | <script lang="ts" setup> |
2 | 2 | import { computed, onMounted, onUnmounted, provide, ref, toRef } from 'vue' |
| 3 | +import { FocusTrap } from 'focus-trap-vue' |
3 | 4 |
|
4 | 5 | import type { DsfrLanguageSelectorElement } from '../DsfrLanguageSelector/DsfrLanguageSelector.vue' |
5 | 6 | import DsfrLanguageSelector from '../DsfrLanguageSelector/DsfrLanguageSelector.vue' |
@@ -80,7 +81,7 @@ const showMenu = () => { |
80 | 81 | // Sans le setTimeout, le focus n'est pas fait |
81 | 82 | setTimeout(() => { |
82 | 83 | document.getElementById('close-button')?.focus() |
83 | | - }) |
| 84 | + }, 50) |
84 | 85 | } |
85 | 86 | const showSearchModal = () => { |
86 | 87 | modalOpened.value = true |
@@ -152,19 +153,25 @@ provide(registerNavigationLinkKey, () => { |
152 | 153 | :title="showSearchLabel" |
153 | 154 | :data-fr-opened="searchModalOpened" |
154 | 155 | @click.prevent.stop="showSearchModal()" |
155 | | - /> |
| 156 | + > |
| 157 | + <span class="fr-sr-only"> |
| 158 | + {{ showSearchLabel }} |
| 159 | + </span> |
| 160 | + </button> |
156 | 161 | <button |
157 | 162 | v-if="isWithSlotNav || quickLinks?.length" |
158 | 163 | id="button-menu" |
159 | 164 | class="fr-btn--menu fr-btn" |
160 | 165 | :data-fr-opened="showMenu" |
161 | 166 | aria-controls="header-navigation" |
162 | 167 | aria-haspopup="dialog" |
163 | | - :aria-label="menuLabel" |
164 | | - :title="menuLabel" |
165 | 168 | data-testid="open-menu-btn" |
166 | 169 | @click.prevent.stop="showMenu()" |
167 | | - /> |
| 170 | + > |
| 171 | + <span class="fr-sr-only"> |
| 172 | + {{ menuLabel }} |
| 173 | + </span> |
| 174 | + </button> |
168 | 175 | </div> |
169 | 176 | </div> |
170 | 177 | <div |
@@ -237,63 +244,74 @@ provide(registerNavigationLinkKey, () => { |
237 | 244 | </div> |
238 | 245 | </div> |
239 | 246 | </div> |
240 | | - <div |
241 | | - v-if="showSearch || isWithSlotNav || (quickLinks && quickLinks.length) || languageSelector" |
242 | | - id="header-navigation" |
243 | | - class="fr-header__menu fr-modal" |
244 | | - :class="{ 'fr-modal--opened': modalOpened }" |
245 | | - :aria-label="menuModalLabel" |
246 | | - role="dialog" |
247 | | - aria-modal="true" |
| 247 | + <FocusTrap |
| 248 | + v-if="modalOpened" |
| 249 | + :active="modalOpened" |
| 250 | + :focus-trap-options="{ |
| 251 | + initialFocus: '#close-button', |
| 252 | + fallbackFocus: '#close-button', |
| 253 | + escapeDeactivates: true, |
| 254 | + clickOutsideDeactivates: true, |
| 255 | + returnFocusOnDeactivate: true, |
| 256 | + }" |
248 | 257 | > |
249 | | - <div class="fr-container"> |
250 | | - <button |
251 | | - id="close-button" |
252 | | - class="fr-btn fr-btn--close" |
253 | | - aria-controls="header-navigation" |
254 | | - data-testid="close-modal-btn" |
255 | | - @click.prevent.stop="hideModal()" |
256 | | - > |
257 | | - {{ closeMenuModalLabel }} |
258 | | - </button> |
259 | | - <div class="fr-header__menu-links"> |
260 | | - <template v-if="languageSelector"> |
261 | | - <DsfrLanguageSelector |
262 | | - v-bind="languageSelector" |
263 | | - @select="languageSelector.currentLanguage = $event.codeIso" |
| 258 | + <div |
| 259 | + v-if="(showSearch || isWithSlotNav || (quickLinks && quickLinks.length) || languageSelector) && modalOpened" |
| 260 | + id="header-navigation" |
| 261 | + class="fr-header__menu fr-modal fr-modal--opened" |
| 262 | + :aria-label="menuModalLabel" |
| 263 | + role="dialog" |
| 264 | + aria-modal="true" |
| 265 | + > |
| 266 | + <div class="fr-container"> |
| 267 | + <button |
| 268 | + id="close-button" |
| 269 | + class="fr-btn fr-btn--close" |
| 270 | + aria-controls="header-navigation" |
| 271 | + data-testid="close-modal-btn" |
| 272 | + @click.prevent.stop="hideModal()" |
| 273 | + > |
| 274 | + {{ closeMenuModalLabel }} |
| 275 | + </button> |
| 276 | + <div class="fr-header__menu-links"> |
| 277 | + <template v-if="languageSelector"> |
| 278 | + <DsfrLanguageSelector |
| 279 | + v-bind="languageSelector" |
| 280 | + @select="languageSelector.currentLanguage = $event.codeIso" |
| 281 | + /> |
| 282 | + </template> |
| 283 | + <slot name="before-quick-links" /> |
| 284 | + <DsfrHeaderMenuLinks |
| 285 | + v-if="menuOpened" |
| 286 | + role="navigation" |
| 287 | + :links="quickLinks" |
| 288 | + :nav-aria-label="quickLinksAriaLabel" |
| 289 | + @link-click="onQuickLinkClick" |
264 | 290 | /> |
265 | | - </template> |
266 | | - <slot name="before-quick-links" /> |
267 | | - <DsfrHeaderMenuLinks |
268 | | - v-if="menuOpened" |
269 | | - role="navigation" |
270 | | - :links="quickLinks" |
271 | | - :nav-aria-label="quickLinksAriaLabel" |
272 | | - @link-click="onQuickLinkClick" |
273 | | - /> |
274 | | - <slot name="after-quick-links" /> |
275 | | - </div> |
| 291 | + <slot name="after-quick-links" /> |
| 292 | + </div> |
276 | 293 |
|
277 | | - <template v-if="modalOpened"> |
278 | | - <slot |
279 | | - name="mainnav" |
280 | | - :hidemodal="hideModal" |
281 | | - /> |
282 | | - </template> |
283 | | - <div |
284 | | - v-if="searchModalOpened" |
285 | | - class="flex justify-center items-center" |
286 | | - > |
287 | | - <DsfrSearchBar |
288 | | - :searchbar-id="searchbarId" |
289 | | - :model-value="modelValue" |
290 | | - :placeholder="placeholder" |
291 | | - @update:model-value="emit('update:modelValue', $event)" |
292 | | - @search="emit('search', $event)" |
293 | | - /> |
| 294 | + <template v-if="modalOpened"> |
| 295 | + <slot |
| 296 | + name="mainnav" |
| 297 | + :hidemodal="hideModal" |
| 298 | + /> |
| 299 | + </template> |
| 300 | + <div |
| 301 | + v-if="searchModalOpened" |
| 302 | + class="flex justify-center items-center" |
| 303 | + > |
| 304 | + <DsfrSearchBar |
| 305 | + :searchbar-id="searchbarId" |
| 306 | + :model-value="modelValue" |
| 307 | + :placeholder="placeholder" |
| 308 | + @update:model-value="emit('update:modelValue', $event)" |
| 309 | + @search="emit('search', $event)" |
| 310 | + /> |
| 311 | + </div> |
294 | 312 | </div> |
295 | 313 | </div> |
296 | | - </div> |
| 314 | + </FocusTrap> |
297 | 315 | <!-- @slot Slot par défaut pour le contenu du fieldset (sera dans `<div class="fr-header__body-row">`) --> |
298 | 316 | <slot /> |
299 | 317 | </div> |
|
0 commit comments