import $ from 'jquery'; import {generateAriaId} from './base.ts'; import type {FomanticInitFunction} from '../../types.ts'; import {queryElems} from '../../utils/dom.ts'; const ariaPatchKey = '_giteaAriaPatchDropdown'; const fomanticDropdownFn = $.fn.dropdown; // use our own `$().dropdown` function to patch Fomantic's dropdown module export function initAriaDropdownPatch() { if ($.fn.dropdown === ariaDropdownFn) throw new Error('initAriaDropdownPatch could only be called once'); $.fn.dropdown.settings.onAfterFiltered = onAfterFiltered; $.fn.dropdown = ariaDropdownFn; $.fn.fomanticExt.onResponseKeepSelectedItem = onResponseKeepSelectedItem; (ariaDropdownFn as FomanticInitFunction).settings = fomanticDropdownFn.settings; } // the patched `$.fn.dropdown` function, it passes the arguments to Fomantic's `$.fn.dropdown` function, and: // * it does the one-time element event attaching on the first call // * it delegates the module internal functions like `onLabelCreate` to the patched functions to add more features. function ariaDropdownFn(this: any, ...args: Parameters) { const ret = fomanticDropdownFn.apply(this, args); for (let el of this) { // dropdown will replace '' // so we need to correctly find the closest '.ui.dropdown' element, it is the real fomantic dropdown module. el = el.closest('.ui.dropdown'); if (!el[ariaPatchKey]) { // the elements don't belong to the dropdown "module" and won't be reset // so we only need to initialize them once. attachInitElements(el); } // if the `$().dropdown()` is called without arguments, or it has non-string (object) argument, // it means that such call will reset the dropdown "module" including internal settings, // then we need to re-delegate the callbacks. const $dropdown = $(el); const dropdownModule = $dropdown.data('module-dropdown'); if (!dropdownModule.giteaDelegated) { dropdownModule.giteaDelegated = true; delegateDropdownModule($dropdown); } } return ret; } // make the item has role=option/menuitem, add an id if there wasn't one yet, make items as non-focusable // the elements inside the dropdown menu item should not be focusable, the focus should always be on the dropdown primary element. function updateMenuItem(dropdown: HTMLElement, item: HTMLElement) { if (!item.id) item.id = generateAriaId(); item.setAttribute('role', (dropdown as any)[ariaPatchKey].listItemRole); item.setAttribute('tabindex', '-1'); for (const el of item.querySelectorAll('a, input, button')) el.setAttribute('tabindex', '-1'); } /** * make the label item and its "delete icon" have correct aria attributes * @param {HTMLElement} label */ function updateSelectionLabel(label: HTMLElement) { // the "label" is like this: "the-label-name " if (!label.id) { label.id = generateAriaId(); } label.tabIndex = -1; const deleteIcon = label.querySelector('.delete.icon'); if (deleteIcon) { deleteIcon.setAttribute('aria-hidden', 'false'); deleteIcon.setAttribute('aria-label', window.config.i18n.remove_label_str.replace('%s', label.getAttribute('data-value'))); deleteIcon.setAttribute('role', 'button'); } } function onAfterFiltered(this: any) { const $dropdown = $(this).closest('.ui.dropdown'); // "this" can be the "ui dropdown" or "