@lekseek/ui
Version:
Vue 3 components library
85 lines (71 loc) • 2.31 kB
text/typescript
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'];
const 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),
};
};
const onEvent = ({ el, event, handler, middleware }: any) => {
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);
}
};
const beforeMount = (el: any, { value }: any) => {
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(() => {
if (!el[HANDLERS_PROPERTY]) {
return;
}
document.documentElement.addEventListener(event, handler, false);
}, 0),
);
};
const unmounted = (el: any) => {
const handlers = el[HANDLERS_PROPERTY] || [];
handlers.forEach(({ event, handler }: any) =>
document.documentElement.removeEventListener(event, handler, false),
);
delete el[HANDLERS_PROPERTY];
};
const 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;