svelisy
Version:
svelisy - DaisyUI components built with Svelte 🌼
132 lines (131 loc) • 5.38 kB
JavaScript
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { bubble, listen, prevent_default, stop_propagation } from 'svelte/internal';
const MODIFIER_DIVIDER = '!';
const modifierRegex = new RegExp(`^[^${MODIFIER_DIVIDER}]+(?:${MODIFIER_DIVIDER}(?:preventDefault|stopPropagation|passive|nonpassive|capture|once|self))+$`);
/** Function for forwarding DOM events to the component's declaration */
export function createEventForwarder(component, except = []) {
// This is our pseudo $on function. It is defined on component mount.
let $on;
// This is a list of events bound before mount.
const events = [];
// And we override the $on function to forward all bound events.
component.$on = (fullEventType, callback) => {
const eventType = fullEventType;
let destructor = () => { };
for (const exception of except) {
if (typeof exception === 'string' && exception === eventType) {
// Bail out of the event forwarding and run the normal Svelte $on() code
const callbacks = component.$$.callbacks[eventType] || (component.$$.callbacks[eventType] = []);
callbacks.push(callback);
return () => {
const index = callbacks.indexOf(callback);
if (index !== -1)
callbacks.splice(index, 1);
};
}
if (typeof exception === 'object' && exception['name'] === eventType) {
const oldCallback = callback;
callback = (...props) => {
if (!(typeof exception === 'object' && exception['shouldExclude']())) {
oldCallback(...props);
}
};
}
}
if ($on) {
// The event was bound programmatically.
destructor = $on(eventType, callback);
}
else {
// The event was bound before mount by Svelte.
events.push([eventType, callback]);
}
return () => {
destructor();
};
};
function forward(e) {
// Internally bubble the event up from Svelte components.
bubble(component, e);
}
return (node) => {
const destructors = [];
const forwardDestructors = {};
// This function is responsible for listening and forwarding
// all bound events.
$on = (fullEventType, callback) => {
let eventType = fullEventType;
let handler = callback;
// DOM addEventListener options argument.
let options = false;
const modifierMatch = eventType.match(modifierRegex);
if (modifierMatch) {
// Parse the event modifiers.
// Supported modifiers:
// - preventDefault
// - stopPropagation
// - passive
// - nonpassive
// - capture
// - once
const parts = eventType.split(MODIFIER_DIVIDER);
eventType = parts[0];
const eventOptions = Object.fromEntries(parts.slice(1).map((mod) => [mod, true]));
if (eventOptions.passive) {
options = options || {};
options.passive = true;
}
if (eventOptions.nonpassive) {
options = options || {};
options.passive = false;
}
if (eventOptions.capture) {
options = options || {};
options.capture = true;
}
if (eventOptions.once) {
options = options || {};
options.once = true;
}
if (eventOptions.preventDefault) {
handler = prevent_default(handler);
}
if (eventOptions.stopPropagation) {
handler = stop_propagation(handler);
}
}
// Listen for the event directly, with the given options.
const off = listen(node, eventType, handler, options);
const destructor = () => {
off();
const idx = destructors.indexOf(destructor);
if (idx > -1) {
destructors.splice(idx, 1);
}
};
destructors.push(destructor);
// Forward the event from Svelte.
if (!(eventType in forwardDestructors)) {
forwardDestructors[eventType] = listen(node, eventType, forward);
}
return destructor;
};
for (let i = 0; i < events.length; i++) {
// Listen to all the events added before mount.
$on(events[i][0], events[i][1]);
}
return {
destroy: () => {
// Remove all event listeners.
for (let i = 0; i < destructors.length; i++) {
destructors[i]();
}
// Remove all event forwarders.
for (const entry of Object.entries(forwardDestructors)) {
entry[1]();
}
}
};
};
}