Create v-click-outside-x.js
This commit is contained in:
parent
5356d04d22
commit
4c249f6e34
1 changed files with 210 additions and 0 deletions
210
src/directives/v-click-outside-x.js
Normal file
210
src/directives/v-click-outside-x.js
Normal file
|
@ -0,0 +1,210 @@
|
|||
const CLICK = 'click';
|
||||
const captureInstances = Object.create(null);
|
||||
const nonCaptureInstances = Object.create(null);
|
||||
const instancesList = [captureInstances, nonCaptureInstances];
|
||||
|
||||
/**
|
||||
* The common event handler for bot capture and non-capture events.
|
||||
*
|
||||
* @param {!Object} context - The event context.
|
||||
* @param {!Object} instances - The capture or non-capture registered instances.
|
||||
* @param {Event} event - The event object.
|
||||
* @returns {undefined} Default.
|
||||
*/
|
||||
const commonHandler = function _onCommonEvent(context, instances, event) {
|
||||
const {target} = event;
|
||||
|
||||
const itemIteratee = function _itemIteratee(item) {
|
||||
const {el} = item;
|
||||
|
||||
if (el !== target && !el.contains(target)) {
|
||||
const {binding} = item;
|
||||
|
||||
if (binding.modifiers.stop) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (binding.modifiers.prevent) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
binding.value.call(context, event);
|
||||
}
|
||||
};
|
||||
|
||||
const keysIteratee = function _keysIteratee(eventName) {
|
||||
return instances[eventName].forEach(itemIteratee);
|
||||
};
|
||||
|
||||
Object.keys(instances).forEach(keysIteratee);
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for capture events.
|
||||
*
|
||||
* @param {Event} event - The event object.
|
||||
*/
|
||||
const captureEventHandler = function onCaptureEvent(event) {
|
||||
/* eslint-disable-next-line babel/no-invalid-this */
|
||||
commonHandler(this, captureInstances, event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for non-capture events.
|
||||
*
|
||||
* @param {Event} event - The event object.
|
||||
*/
|
||||
const nonCaptureEventHandler = function onNonCaptureEvent(event) {
|
||||
/* eslint-disable-next-line babel/no-invalid-this */
|
||||
commonHandler(this, nonCaptureInstances, event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the correct event handler: Capture or non-capture.
|
||||
*
|
||||
* @param {boolean} useCapture - Indicate which handler to use; 'true' to use
|
||||
* capture handler or 'false' for non-capture.
|
||||
* @returns {Function} - The event handler.
|
||||
*/
|
||||
const getEventHandler = function _getEventHandler(useCapture) {
|
||||
return useCapture ? captureEventHandler : nonCaptureEventHandler;
|
||||
};
|
||||
|
||||
/**
|
||||
* The directive definition.
|
||||
* {@link https://vuejs.org/v2/guide/custom-directive.html|Custom directive}
|
||||
*
|
||||
* @namespace
|
||||
* @property {!Object} $_captureInstances - Registered capture instances.
|
||||
* @property {!Object} $_nonCaptureInstances - Registered non-capture instances.
|
||||
* @property {Function} $_onCaptureEvent - Event handler for capture events.
|
||||
* @property {Function} $_onNonCaptureEvent - Event handler for non-capture events.
|
||||
* @property {Function} bind - Called only once, when the directive is first
|
||||
* bound to the element.
|
||||
* @property {Function} unbind - Called only once, when the directive is unbound
|
||||
* from the element.
|
||||
* @property {string} version - The version number of this release.
|
||||
*/
|
||||
export const directive = Object.defineProperties(
|
||||
{},
|
||||
{
|
||||
$_captureInstances: {
|
||||
value: captureInstances,
|
||||
},
|
||||
|
||||
$_nonCaptureInstances: {
|
||||
value: nonCaptureInstances,
|
||||
},
|
||||
|
||||
$_onCaptureEvent: {
|
||||
value: captureEventHandler,
|
||||
},
|
||||
|
||||
$_onNonCaptureEvent: {
|
||||
value: nonCaptureEventHandler,
|
||||
},
|
||||
|
||||
bind: {
|
||||
value: function bind(el, binding) {
|
||||
if (typeof binding.value !== 'function') {
|
||||
throw new TypeError('Binding value must be a function.');
|
||||
}
|
||||
|
||||
const arg = binding.arg || CLICK;
|
||||
const normalisedBinding = {
|
||||
...binding,
|
||||
...{
|
||||
arg,
|
||||
modifiers: {
|
||||
...{
|
||||
capture: false,
|
||||
prevent: false,
|
||||
stop: false,
|
||||
},
|
||||
...binding.modifiers,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const useCapture = normalisedBinding.modifiers.capture;
|
||||
const instances = useCapture ? captureInstances : nonCaptureInstances;
|
||||
|
||||
if (!Array.isArray(instances[arg])) {
|
||||
instances[arg] = [];
|
||||
}
|
||||
|
||||
if (instances[arg].push({el, binding: normalisedBinding}) === 1) {
|
||||
if (typeof document === 'object' && document) {
|
||||
document.addEventListener(
|
||||
arg,
|
||||
getEventHandler(useCapture),
|
||||
useCapture,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
unbind: {
|
||||
value: function unbind(el) {
|
||||
const compareElements = function _compareElements(item) {
|
||||
return item.el !== el;
|
||||
};
|
||||
|
||||
const instancesIteratee = function _instancesIteratee(instances) {
|
||||
const instanceKeys = Object.keys(instances);
|
||||
|
||||
if (instanceKeys.length) {
|
||||
const useCapture = instances === captureInstances;
|
||||
|
||||
const keysIteratee = function _keysIteratee(eventName) {
|
||||
const newInstance = instances[eventName].filter(compareElements);
|
||||
|
||||
if (newInstance.length) {
|
||||
instances[eventName] = newInstance;
|
||||
} else {
|
||||
if (typeof document === 'object' && document) {
|
||||
document.removeEventListener(
|
||||
eventName,
|
||||
getEventHandler(useCapture),
|
||||
useCapture,
|
||||
);
|
||||
}
|
||||
|
||||
delete instances[eventName];
|
||||
}
|
||||
};
|
||||
|
||||
instanceKeys.forEach(keysIteratee);
|
||||
}
|
||||
};
|
||||
|
||||
instancesList.forEach(instancesIteratee);
|
||||
},
|
||||
},
|
||||
|
||||
/* Note: This needs to be manually updated to match package.json. */
|
||||
version: {
|
||||
enumerable: true,
|
||||
value: '3.7.1',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* @typedef {Function} Vue - The constructor.
|
||||
* @property {Function} directive - You can register a global custom directive
|
||||
* with the Vue.directive() method, passing in a directiveID followed by a
|
||||
* definition object.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A Vue.js plugin should expose an install method. The method will be called
|
||||
* with the Vue constructor as the first argument, along with possible options.
|
||||
* {@link https://vuejs.org/v2/guide/plugins.html#Writing-a-Plugin|Writing a plugin}.
|
||||
*
|
||||
* @param {Vue} Vue - The Vue function.
|
||||
*/
|
||||
export function install(Vue) {
|
||||
Vue.directive('click-outside', directive);
|
||||
}
|
Loading…
Add table
Reference in a new issue