|
@@ -729,9 +729,13 @@
|
|
|
}
|
|
|
|
|
|
function registerListener(component, el, event, modifiers, expression, extraVars = {}) {
|
|
|
+ const options = {
|
|
|
+ passive: modifiers.includes('passive')
|
|
|
+ };
|
|
|
+
|
|
|
if (modifiers.includes('away')) {
|
|
|
let handler = e => {
|
|
|
- // Don't do anything if the click came form the element or within it.
|
|
|
+ // Don't do anything if the click came from the element or within it.
|
|
|
if (el.contains(e.target)) return; // Don't do anything if this element isn't currently visible.
|
|
|
|
|
|
if (el.offsetWidth < 1 && el.offsetHeight < 1) return; // Now that we are sure the element is visible, AND the click
|
|
@@ -740,12 +744,12 @@
|
|
|
runListenerHandler(component, expression, e, extraVars);
|
|
|
|
|
|
if (modifiers.includes('once')) {
|
|
|
- document.removeEventListener(event, handler);
|
|
|
+ document.removeEventListener(event, handler, options);
|
|
|
}
|
|
|
}; // Listen for this event at the root level.
|
|
|
|
|
|
|
|
|
- document.addEventListener(event, handler);
|
|
|
+ document.addEventListener(event, handler, options);
|
|
|
} else {
|
|
|
let listenerTarget = modifiers.includes('window') ? window : modifiers.includes('document') ? document : el;
|
|
|
|
|
@@ -754,7 +758,7 @@
|
|
|
// has been removed. It's now stale.
|
|
|
if (listenerTarget === window || listenerTarget === document) {
|
|
|
if (!document.body.contains(el)) {
|
|
|
- listenerTarget.removeEventListener(event, handler);
|
|
|
+ listenerTarget.removeEventListener(event, handler, options);
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
@@ -777,7 +781,7 @@
|
|
|
e.preventDefault();
|
|
|
} else {
|
|
|
if (modifiers.includes('once')) {
|
|
|
- listenerTarget.removeEventListener(event, handler);
|
|
|
+ listenerTarget.removeEventListener(event, handler, options);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -789,7 +793,7 @@
|
|
|
handler = debounce(handler, wait);
|
|
|
}
|
|
|
|
|
|
- listenerTarget.addEventListener(event, handler);
|
|
|
+ listenerTarget.addEventListener(event, handler, options);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1306,12 +1310,12 @@
|
|
|
}
|
|
|
|
|
|
class Component {
|
|
|
- constructor(el, seedDataForCloning = null) {
|
|
|
+ constructor(el, componentForClone = null) {
|
|
|
this.$el = el;
|
|
|
const dataAttr = this.$el.getAttribute('x-data');
|
|
|
const dataExpression = dataAttr === '' ? '{}' : dataAttr;
|
|
|
const initExpression = this.$el.getAttribute('x-init');
|
|
|
- this.unobservedData = seedDataForCloning ? seedDataForCloning : saferEval(dataExpression, {
|
|
|
+ this.unobservedData = componentForClone ? componentForClone.getUnobservedData() : saferEval(dataExpression, {
|
|
|
$el: this.$el
|
|
|
});
|
|
|
// Construct a Proxy-based observable. This will be used to handle reactivity.
|
|
@@ -1339,11 +1343,20 @@
|
|
|
this.watchers[property].push(callback);
|
|
|
};
|
|
|
|
|
|
+ let canonicalComponentElementReference = componentForClone ? componentForClone.$el : this.$el; // Register custom magic properties.
|
|
|
+
|
|
|
+ Object.entries(Alpine.magicProperties).forEach(([name, callback]) => {
|
|
|
+ Object.defineProperty(this.unobservedData, `$${name}`, {
|
|
|
+ get: function get() {
|
|
|
+ return callback(canonicalComponentElementReference);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
this.showDirectiveStack = [];
|
|
|
this.showDirectiveLastElement;
|
|
|
var initReturnedCallback; // If x-init is present AND we aren't cloning (skip x-init on clone)
|
|
|
|
|
|
- if (initExpression && !seedDataForCloning) {
|
|
|
+ if (initExpression && !componentForClone) {
|
|
|
// We want to allow data manipulation, but not trigger DOM updates just yet.
|
|
|
// We haven't even initialized the elements with their Alpine bindings. I mean c'mon.
|
|
|
this.pauseReactivity = true;
|
|
@@ -1612,7 +1625,7 @@
|
|
|
if (!(closestParentComponent && closestParentComponent.isSameNode(this.$el))) continue;
|
|
|
|
|
|
if (mutations[i].type === 'attributes' && mutations[i].attributeName === 'x-data') {
|
|
|
- const rawData = saferEval(mutations[i].target.getAttribute('x-data'), {
|
|
|
+ const rawData = saferEval(mutations[i].target.getAttribute('x-data') || '{}', {
|
|
|
$el: this.$el
|
|
|
});
|
|
|
Object.keys(rawData).forEach(key => {
|
|
@@ -1669,6 +1682,7 @@
|
|
|
const Alpine = {
|
|
|
version: "2.4.1",
|
|
|
pauseMutationObserver: false,
|
|
|
+ magicProperties: {},
|
|
|
start: async function start() {
|
|
|
if (!isTesting()) {
|
|
|
await domReady();
|
|
@@ -1742,8 +1756,11 @@
|
|
|
},
|
|
|
clone: function clone(component, newEl) {
|
|
|
if (!newEl.__x) {
|
|
|
- newEl.__x = new Component(newEl, component.getUnobservedData());
|
|
|
+ newEl.__x = new Component(newEl, component);
|
|
|
}
|
|
|
+ },
|
|
|
+ addMagicProperty: function addMagicProperty(name, callback) {
|
|
|
+ this.magicProperties[name] = callback;
|
|
|
}
|
|
|
};
|
|
|
|