summaryrefslogtreecommitdiff
path: root/node_modules/delegate-it/delegate.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/delegate-it/delegate.js')
-rw-r--r--node_modules/delegate-it/delegate.js66
1 files changed, 66 insertions, 0 deletions
diff --git a/node_modules/delegate-it/delegate.js b/node_modules/delegate-it/delegate.js
new file mode 100644
index 00000000..c73b8ec7
--- /dev/null
+++ b/node_modules/delegate-it/delegate.js
@@ -0,0 +1,66 @@
+/** Keeps track of raw listeners added to the base elements to avoid duplication */
+const ledger = new WeakMap();
+function editLedger(wanted, baseElement, callback, setup) {
+ if (!wanted && !ledger.has(baseElement)) {
+ return false;
+ }
+ const elementMap = ledger.get(baseElement)
+ ?? new WeakMap();
+ ledger.set(baseElement, elementMap);
+ const setups = elementMap.get(callback) ?? new Set();
+ elementMap.set(callback, setups);
+ const existed = setups.has(setup);
+ if (wanted) {
+ setups.add(setup);
+ }
+ else {
+ setups.delete(setup);
+ }
+ return existed && wanted;
+}
+function safeClosest(event, selector) {
+ let target = event.target;
+ if (target instanceof Text) {
+ target = target.parentElement;
+ }
+ if (target instanceof Element && event.currentTarget instanceof Element) {
+ // `.closest()` may match ancestors of `currentTarget` but we only need its children
+ const closest = target.closest(selector);
+ if (closest && event.currentTarget.contains(closest)) {
+ return closest;
+ }
+ }
+}
+// This type isn't exported as a declaration, so it needs to be duplicated above
+function delegate(selector, type, callback, options = {}) {
+ const { signal, base = document } = options;
+ if (signal?.aborted) {
+ return;
+ }
+ // Don't pass `once` to `addEventListener` because it needs to be handled in `delegate-it`
+ const { once, ...nativeListenerOptions } = options;
+ // `document` should never be the base, it's just an easy way to define "global event listeners"
+ const baseElement = base instanceof Document ? base.documentElement : base;
+ // Handle the regular Element usage
+ const capture = Boolean(typeof options === 'object' ? options.capture : options);
+ const listenerFunction = (event) => {
+ const delegateTarget = safeClosest(event, String(selector));
+ if (delegateTarget) {
+ const delegateEvent = Object.assign(event, { delegateTarget });
+ callback.call(baseElement, delegateEvent);
+ if (once) {
+ baseElement.removeEventListener(type, listenerFunction, nativeListenerOptions);
+ editLedger(false, baseElement, callback, setup);
+ }
+ }
+ };
+ const setup = JSON.stringify({ selector, type, capture });
+ const isAlreadyListening = editLedger(true, baseElement, callback, setup);
+ if (!isAlreadyListening) {
+ baseElement.addEventListener(type, listenerFunction, nativeListenerOptions);
+ }
+ signal?.addEventListener('abort', () => {
+ editLedger(false, baseElement, callback, setup);
+ });
+}
+export default delegate;