UNPKG

vuetify-wcag

Version:

VuetifyJS but then WCAG/A11Y compatible

110 lines (90 loc) 3.78 kB
import Vue from 'vue' export type MouseHandler = (e: MouseEvent | TouchEvent) => any export type MouseEvents = { [event: string]: { event: string passive?: boolean capture?: boolean once?: boolean stop?: boolean prevent?: boolean button?: number result?: any } } export type MouseEventsMap = { [event: string]: MouseHandler | MouseHandler[] } export default Vue.extend({ name: 'mouse', methods: { getDefaultMouseEventHandlers (suffix: string, getEvent: MouseHandler): MouseEventsMap { const listeners = Object.keys(this.$listeners) .filter(key => key.endsWith(suffix)) .reduce((acc, key) => { acc[key] = { event: key.slice(0, -suffix.length) } return acc }, {} as MouseEvents) return this.getMouseEventHandlers({ ...listeners, ['contextmenu' + suffix]: { event: 'contextmenu', prevent: true, result: false }, }, getEvent) }, getMouseEventHandlers (events: MouseEvents, getEvent: MouseHandler): MouseEventsMap { const on: MouseEventsMap = {} for (const event in events) { const eventOptions = events[event] if (!this.$listeners[event]) continue // TODO somehow pull in modifiers const prefix = eventOptions.passive ? '&' : ((eventOptions.once ? '~' : '') + (eventOptions.capture ? '!' : '')) const key = prefix + eventOptions.event const handler: MouseHandler = e => { const mouseEvent: MouseEvent = e as MouseEvent if (eventOptions.button === undefined || (mouseEvent.buttons > 0 && mouseEvent.button === eventOptions.button)) { if (eventOptions.prevent) { e.preventDefault() } if (eventOptions.stop) { e.stopPropagation() } // Due to TouchEvent target always returns the element that is first placed // Even if touch point has since moved outside the interactive area of that element // Ref: https://developer.mozilla.org/en-US/docs/Web/API/Touch/target // This block of code aims to make sure touchEvent is always dispatched from the element that is being pointed at if (e && 'touches' in e) { const classSeparator = ' ' const eventTargetClasses = (e.currentTarget as HTMLElement)?.className.split(classSeparator) const currentTargets = document.elementsFromPoint(e.changedTouches[0].clientX, e.changedTouches[0].clientY) // Get "the same kind" current hovering target by checking // If element has the same class of initial touch start element (which has touch event listener registered) const currentTarget = currentTargets.find(t => t.className.split(classSeparator).some(c => eventTargetClasses.includes(c))) if (currentTarget && !(e.target as HTMLElement)?.isSameNode(currentTarget) ) { currentTarget.dispatchEvent(new TouchEvent(e.type, { changedTouches: e.changedTouches as unknown as Touch[], targetTouches: e.targetTouches as unknown as Touch[], touches: e.touches as unknown as Touch[], })) return } } this.$emit(event, getEvent(e), e) } return eventOptions.result } if (key in on) { /* istanbul ignore next */ if (Array.isArray(on[key])) { (on[key] as MouseHandler[]).push(handler) } else { on[key] = [on[key], handler] as MouseHandler[] } } else { on[key] = handler } } return on }, }, })