UNPKG

@lekseek/ui

Version:

Vue 3 components library

93 lines (79 loc) 2.76 kB
import { Directive } from 'vue'; const HANDLERS_PROPERTY = '__v-click-outside'; const HAS_WINDOWS = typeof window !== 'undefined'; const HAS_NAVIGATOR = typeof navigator !== 'undefined'; const IS_TOUCH = HAS_WINDOWS && ('ontouchstart' in window || (HAS_NAVIGATOR && (navigator as any).msMaxTouchPoints > 0)); const EVENTS = IS_TOUCH ? ['touchstart'] : ['click']; function processDirectiveArguments (bindingValue: any) { const isFunction = typeof bindingValue === 'function'; if (!isFunction && typeof bindingValue !== 'object') { throw new Error( 'v-click-outside: Binding value must be a function or an object', ); } return { handler: isFunction ? bindingValue : bindingValue.handler, middleware: bindingValue.middleware || ((item: any) => item), events: bindingValue.events || EVENTS, isActive: !(bindingValue.isActive === false), }; } function onEvent ({ el, event, handler, middleware }: any) { // Note: composedPath is not supported on IE and Edge, more information here: // https://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath // In the meanwhile, we are using el.contains for those browsers, not // the ideal solution, but using IE or EDGE is not ideal either. const path = event.path || (event.composedPath && event.composedPath()); const isClickOutside = path ? path.indexOf(el) < 0 : !el.contains(event.target); if (!isClickOutside) { return; } if (middleware(event)) { handler(event); } } function beforeMount (el: any, { value }: any): void { const { events, handler, middleware, isActive } = processDirectiveArguments( value, ); if (!isActive) { return; } el[HANDLERS_PROPERTY] = events.map((eventName: any) => ({ event: eventName, handler: (event: any) => onEvent({ event, el, handler, middleware }), })); el[HANDLERS_PROPERTY].forEach(({ event, handler }: any) => setTimeout(() => { // Note: More info about this implementation can be found here: // https://github.com/ndelvalle/v-click-outside/issues/137 if (!el[HANDLERS_PROPERTY]) { return; } document.documentElement.addEventListener(event, handler, false); }, 0), ); } function unmounted (el: any) { const handlers = el[HANDLERS_PROPERTY] || []; handlers.forEach(({ event, handler }: any) => document.documentElement.removeEventListener(event, handler, false), ); delete el[HANDLERS_PROPERTY]; } function updated (el: any, { value, oldValue }: any) { if (JSON.stringify(value) === JSON.stringify(oldValue)) { return; } unmounted(el); beforeMount(el, { value }); } const directive: Directive = { beforeMount, updated, unmounted, }; export default directive;