|
| 1 | +import {closest, getAttr, getById, matches, select} from '../../../utils/dom' |
| 2 | + |
| 3 | +const TABLE_TAG_NAMES = ['TD', 'TH', 'TR'] |
| 4 | + |
| 5 | +// Filter CSS selector for click/dblclick/etc. events |
| 6 | +// If any of these selectors match the clicked element, we ignore the event |
| 7 | +export const EVENT_FILTER = [ |
| 8 | + 'a', |
| 9 | + 'a *', // Include content inside links |
| 10 | + 'button', |
| 11 | + 'button *', // Include content inside buttons |
| 12 | + 'input:not(.disabled):not([disabled])', |
| 13 | + 'select:not(.disabled):not([disabled])', |
| 14 | + 'textarea:not(.disabled):not([disabled])', |
| 15 | + '[role="link"]', |
| 16 | + '[role="link"] *', |
| 17 | + '[role="button"]', |
| 18 | + '[role="button"] *', |
| 19 | + '[tabindex]:not(.disabled):not([disabled])', |
| 20 | +].join(',') |
| 21 | + |
| 22 | +// Returns `true` if we should ignore the click/double-click/keypress event |
| 23 | +// Avoids having the user need to use `@click.stop` on the form control |
| 24 | +export const filterEvent = (event: Event) => { |
| 25 | + // Exit early when we don't have a target element |
| 26 | + if (!event || !event.target) { |
| 27 | + /* istanbul ignore next */ |
| 28 | + return false |
| 29 | + } |
| 30 | + const el = event.target as HTMLElement |
| 31 | + // Exit early when element is disabled or a table element |
| 32 | + if (('disabled' in el && (el as any).disabled) || TABLE_TAG_NAMES.indexOf(el.tagName) !== -1) { |
| 33 | + return false |
| 34 | + } |
| 35 | + // Ignore the click when it was inside a dropdown menu |
| 36 | + if (closest('.dropdown-menu', el)) { |
| 37 | + return true |
| 38 | + } |
| 39 | + const label = el.tagName === 'LABEL' ? el : closest('label', el) |
| 40 | + // If the label's form control is not disabled then we don't propagate event |
| 41 | + // Modern browsers have `label.control` that references the associated input, but IE 11 |
| 42 | + // does not have this property on the label element, so we resort to DOM lookups |
| 43 | + if (label) { |
| 44 | + const labelFor = getAttr(label, 'for') |
| 45 | + const input = labelFor ? getById(labelFor) : select('input, select, textarea', label) |
| 46 | + if (input && !input.disabled) { |
| 47 | + return true |
| 48 | + } |
| 49 | + } |
| 50 | + // Otherwise check if the event target matches one of the selectors in the |
| 51 | + // event filter (i.e. anchors, non disabled inputs, etc.) |
| 52 | + // Return `true` if we should ignore the event |
| 53 | + return matches(el, EVENT_FILTER) |
| 54 | +} |
0 commit comments