mirror of https://github.com/sveltejs/svelte
Actions add additional functionality to elements within your component's template that may be difficult to add with other mechanisms. Examples of functionality which actions makes trivial to attach are: * tooltips * image lazy loaders * drag and drop functionality Actions can be added to an element with the `use` directive. ```html <img use:lazyload data-src="giant-photo.jpg> ``` Data may be passed to the action as an object literal (e.g. `use:b="{ setting: true }"`, a literal value (e.g. `use:b="'a string'"`), or a value or function from your component's state (e.g. `add:b="foo"` or `add:b="foo()"`). Actions are defined in a "actions" property on your component definition. ```html <script> export default { actions: { b(node, data) { // do something return { update(data) {}, destroy() {} } } } } </script> ``` A action is a function which receives a reference to an element and optionally the data if it is added in the HTML. This function can then attach listeners or alter the element as needed. The action can optionally return an object with the methods `update(data)` and `destroy()`. When data is added in the HTML and comes from state, the action's `update(data)` will be called if defined whenever the state is changed. When the element is removed from the DOM `destroy()` will be called if provided, allowing for cleanup of event listeners, etc. See https://github.com/sveltejs/svelte/issues/469 for discussion around this feature and more examples of how it could be used.pull/1247/head
parent
bc416a538f
commit
04f5d5c975
@ -0,0 +1,7 @@
|
||||
import Node from './shared/Node';
|
||||
|
||||
export default class Action extends Node {
|
||||
name: string;
|
||||
value: Node[]
|
||||
expression: Node
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import checkForDupes from '../utils/checkForDupes';
|
||||
import checkForComputedKeys from '../utils/checkForComputedKeys';
|
||||
import { Validator } from '../../';
|
||||
import { Node } from '../../../interfaces';
|
||||
|
||||
export default function actions(validator: Validator, prop: Node) {
|
||||
if (prop.value.type !== 'ObjectExpression') {
|
||||
validator.error(
|
||||
`The 'actions' property must be an object literal`,
|
||||
prop
|
||||
);
|
||||
}
|
||||
|
||||
checkForDupes(validator, prop.value.properties);
|
||||
checkForComputedKeys(validator, prop.value.properties);
|
||||
}
|
@ -0,0 +1,246 @@
|
||||
function noop() {}
|
||||
|
||||
function assign(target) {
|
||||
var k,
|
||||
source,
|
||||
i = 1,
|
||||
len = arguments.length;
|
||||
for (; i < len; i++) {
|
||||
source = arguments[i];
|
||||
for (k in source) target[k] = source[k];
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
function insertNode(node, target, anchor) {
|
||||
target.insertBefore(node, anchor);
|
||||
}
|
||||
|
||||
function detachNode(node) {
|
||||
node.parentNode.removeChild(node);
|
||||
}
|
||||
|
||||
function createElement(name) {
|
||||
return document.createElement(name);
|
||||
}
|
||||
|
||||
function blankObject() {
|
||||
return Object.create(null);
|
||||
}
|
||||
|
||||
function destroy(detach) {
|
||||
this.destroy = noop;
|
||||
this.fire('destroy');
|
||||
this.set = this.get = noop;
|
||||
|
||||
if (detach !== false) this._fragment.u();
|
||||
this._fragment.d();
|
||||
this._fragment = this._state = null;
|
||||
}
|
||||
|
||||
function _differs(a, b) {
|
||||
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
||||
}
|
||||
|
||||
function dispatchObservers(component, group, changed, newState, oldState) {
|
||||
for (var key in group) {
|
||||
if (!changed[key]) continue;
|
||||
|
||||
var newValue = newState[key];
|
||||
var oldValue = oldState[key];
|
||||
|
||||
var callbacks = group[key];
|
||||
if (!callbacks) continue;
|
||||
|
||||
for (var i = 0; i < callbacks.length; i += 1) {
|
||||
var callback = callbacks[i];
|
||||
if (callback.__calling) continue;
|
||||
|
||||
callback.__calling = true;
|
||||
callback.call(component, newValue, oldValue);
|
||||
callback.__calling = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fire(eventName, data) {
|
||||
var handlers =
|
||||
eventName in this._handlers && this._handlers[eventName].slice();
|
||||
if (!handlers) return;
|
||||
|
||||
for (var i = 0; i < handlers.length; i += 1) {
|
||||
handlers[i].call(this, data);
|
||||
}
|
||||
}
|
||||
|
||||
function get(key) {
|
||||
return key ? this._state[key] : this._state;
|
||||
}
|
||||
|
||||
function init(component, options) {
|
||||
component._observers = { pre: blankObject(), post: blankObject() };
|
||||
component._handlers = blankObject();
|
||||
component._bind = options._bind;
|
||||
|
||||
component.options = options;
|
||||
component.root = options.root || component;
|
||||
component.store = component.root.store || options.store;
|
||||
}
|
||||
|
||||
function observe(key, callback, options) {
|
||||
var group = options && options.defer
|
||||
? this._observers.post
|
||||
: this._observers.pre;
|
||||
|
||||
(group[key] || (group[key] = [])).push(callback);
|
||||
|
||||
if (!options || options.init !== false) {
|
||||
callback.__calling = true;
|
||||
callback.call(this, this._state[key]);
|
||||
callback.__calling = false;
|
||||
}
|
||||
|
||||
return {
|
||||
cancel: function() {
|
||||
var index = group[key].indexOf(callback);
|
||||
if (~index) group[key].splice(index, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function on(eventName, handler) {
|
||||
if (eventName === 'teardown') return this.on('destroy', handler);
|
||||
|
||||
var handlers = this._handlers[eventName] || (this._handlers[eventName] = []);
|
||||
handlers.push(handler);
|
||||
|
||||
return {
|
||||
cancel: function() {
|
||||
var index = handlers.indexOf(handler);
|
||||
if (~index) handlers.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function set(newState) {
|
||||
this._set(assign({}, newState));
|
||||
if (this.root._lock) return;
|
||||
this.root._lock = true;
|
||||
callAll(this.root._beforecreate);
|
||||
callAll(this.root._oncreate);
|
||||
callAll(this.root._aftercreate);
|
||||
this.root._lock = false;
|
||||
}
|
||||
|
||||
function _set(newState) {
|
||||
var oldState = this._state,
|
||||
changed = {},
|
||||
dirty = false;
|
||||
|
||||
for (var key in newState) {
|
||||
if (this._differs(newState[key], oldState[key])) changed[key] = dirty = true;
|
||||
}
|
||||
if (!dirty) return;
|
||||
|
||||
this._state = assign({}, oldState, newState);
|
||||
this._recompute(changed, this._state);
|
||||
if (this._bind) this._bind(changed, this._state);
|
||||
|
||||
if (this._fragment) {
|
||||
dispatchObservers(this, this._observers.pre, changed, this._state, oldState);
|
||||
this._fragment.p(changed, this._state);
|
||||
dispatchObservers(this, this._observers.post, changed, this._state, oldState);
|
||||
}
|
||||
}
|
||||
|
||||
function callAll(fns) {
|
||||
while (fns && fns.length) fns.shift()();
|
||||
}
|
||||
|
||||
function _mount(target, anchor) {
|
||||
this._fragment[this._fragment.i ? 'i' : 'm'](target, anchor || null);
|
||||
}
|
||||
|
||||
function _unmount() {
|
||||
if (this._fragment) this._fragment.u();
|
||||
}
|
||||
|
||||
var proto = {
|
||||
destroy: destroy,
|
||||
get: get,
|
||||
fire: fire,
|
||||
observe: observe,
|
||||
on: on,
|
||||
set: set,
|
||||
teardown: destroy,
|
||||
_recompute: noop,
|
||||
_set: _set,
|
||||
_mount: _mount,
|
||||
_unmount: _unmount,
|
||||
_differs: _differs
|
||||
};
|
||||
|
||||
/* generated by Svelte vX.Y.Z */
|
||||
|
||||
function link(node) {
|
||||
|
||||
function onClick(event) {
|
||||
event.preventDefault();
|
||||
history.pushState(null, null, event.target.href);
|
||||
}
|
||||
|
||||
node.addEventListener('click', onClick);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
node.removeEventListener('click', onClick);
|
||||
}
|
||||
}
|
||||
}
|
||||
function create_main_fragment(component, state) {
|
||||
var a, link_action;
|
||||
|
||||
return {
|
||||
c: function create() {
|
||||
a = createElement("a");
|
||||
a.textContent = "Test";
|
||||
this.h();
|
||||
},
|
||||
|
||||
h: function hydrate() {
|
||||
a.href = "#";
|
||||
link_action = link(a) || {};
|
||||
},
|
||||
|
||||
m: function mount(target, anchor) {
|
||||
insertNode(a, target, anchor);
|
||||
},
|
||||
|
||||
p: noop,
|
||||
|
||||
u: function unmount() {
|
||||
detachNode(a);
|
||||
},
|
||||
|
||||
d: function destroy$$1() {
|
||||
if (typeof link_action.destroy === 'function') link_action.destroy();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function SvelteComponent(options) {
|
||||
init(this, options);
|
||||
this._state = assign({}, options.data);
|
||||
|
||||
this._fragment = create_main_fragment(this, this._state);
|
||||
|
||||
if (options.target) {
|
||||
this._fragment.c();
|
||||
this._mount(options.target, options.anchor);
|
||||
}
|
||||
}
|
||||
|
||||
assign(SvelteComponent.prototype, proto);
|
||||
|
||||
export default SvelteComponent;
|
@ -0,0 +1,64 @@
|
||||
/* generated by Svelte vX.Y.Z */
|
||||
import { assign, createElement, detachNode, init, insertNode, noop, proto } from "svelte/shared.js";
|
||||
|
||||
function link(node) {
|
||||
|
||||
function onClick(event) {
|
||||
event.preventDefault();
|
||||
history.pushState(null, null, event.target.href);
|
||||
}
|
||||
|
||||
node.addEventListener('click', onClick);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
node.removeEventListener('click', onClick);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function create_main_fragment(component, state) {
|
||||
var a, link_action;
|
||||
|
||||
return {
|
||||
c: function create() {
|
||||
a = createElement("a");
|
||||
a.textContent = "Test";
|
||||
this.h();
|
||||
},
|
||||
|
||||
h: function hydrate() {
|
||||
a.href = "#";
|
||||
link_action = link(a) || {};
|
||||
},
|
||||
|
||||
m: function mount(target, anchor) {
|
||||
insertNode(a, target, anchor);
|
||||
},
|
||||
|
||||
p: noop,
|
||||
|
||||
u: function unmount() {
|
||||
detachNode(a);
|
||||
},
|
||||
|
||||
d: function destroy() {
|
||||
if (typeof link_action.destroy === 'function') link_action.destroy();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function SvelteComponent(options) {
|
||||
init(this, options);
|
||||
this._state = assign({}, options.data);
|
||||
|
||||
this._fragment = create_main_fragment(this, this._state);
|
||||
|
||||
if (options.target) {
|
||||
this._fragment.c();
|
||||
this._mount(options.target, options.anchor);
|
||||
}
|
||||
}
|
||||
|
||||
assign(SvelteComponent.prototype, proto);
|
||||
export default SvelteComponent;
|
@ -0,0 +1,23 @@
|
||||
<a href="#" use:link>Test</a>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
actions: {
|
||||
link(node) {
|
||||
|
||||
function onClick(event) {
|
||||
event.preventDefault();
|
||||
history.pushState(null, null, event.target.href);
|
||||
}
|
||||
|
||||
node.addEventListener('click', onClick);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
node.removeEventListener('click', onClick);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -0,0 +1 @@
|
||||
<input use:tooltip="t('tooltip msg')">
|
@ -0,0 +1,47 @@
|
||||
{
|
||||
"hash": 1937205193,
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 38,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 38,
|
||||
"type": "Element",
|
||||
"name": "input",
|
||||
"attributes": [
|
||||
{
|
||||
"start": 7,
|
||||
"end": 37,
|
||||
"type": "Action",
|
||||
"name": "tooltip",
|
||||
"expression": {
|
||||
"type": "CallExpression",
|
||||
"start": 20,
|
||||
"end": 36,
|
||||
"callee": {
|
||||
"type": "Identifier",
|
||||
"start": 20,
|
||||
"end": 21,
|
||||
"name": "t"
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"type": "Literal",
|
||||
"start": 22,
|
||||
"end": 35,
|
||||
"value": "tooltip msg",
|
||||
"raw": "'tooltip msg'"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": null,
|
||||
"js": null
|
||||
}
|
@ -0,0 +1 @@
|
||||
<input use:tooltip="message">
|
@ -0,0 +1,33 @@
|
||||
{
|
||||
"hash": 1937205193,
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 29,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 29,
|
||||
"type": "Element",
|
||||
"name": "input",
|
||||
"attributes": [
|
||||
{
|
||||
"start": 7,
|
||||
"end": 28,
|
||||
"type": "Action",
|
||||
"name": "tooltip",
|
||||
"expression": {
|
||||
"type": "Identifier",
|
||||
"start": 20,
|
||||
"end": 27,
|
||||
"name": "message"
|
||||
}
|
||||
}
|
||||
],
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": null,
|
||||
"js": null
|
||||
}
|
@ -0,0 +1 @@
|
||||
<input use:tooltip="'tooltip msg'">
|
@ -0,0 +1,34 @@
|
||||
{
|
||||
"hash": 1937205193,
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 35,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 35,
|
||||
"type": "Element",
|
||||
"name": "input",
|
||||
"attributes": [
|
||||
{
|
||||
"start": 7,
|
||||
"end": 34,
|
||||
"type": "Action",
|
||||
"name": "tooltip",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"start": 20,
|
||||
"end": 33,
|
||||
"value": "tooltip msg",
|
||||
"raw": "'tooltip msg'"
|
||||
}
|
||||
}
|
||||
],
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": null,
|
||||
"js": null
|
||||
}
|
@ -0,0 +1 @@
|
||||
<input use:autofocus>
|
@ -0,0 +1,28 @@
|
||||
{
|
||||
"hash": 1937205193,
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 21,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 21,
|
||||
"type": "Element",
|
||||
"name": "input",
|
||||
"attributes": [
|
||||
{
|
||||
"start": 7,
|
||||
"end": 20,
|
||||
"type": "Action",
|
||||
"name": "autofocus",
|
||||
"expression": null
|
||||
}
|
||||
],
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"css": null,
|
||||
"js": null
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
html: `
|
||||
<button>action</button>
|
||||
`,
|
||||
|
||||
test ( assert, component, target, window ) {
|
||||
const button = target.querySelector( 'button' );
|
||||
const eventEnter = new window.MouseEvent( 'mouseenter' );
|
||||
const eventLeave = new window.MouseEvent( 'mouseleave' );
|
||||
|
||||
button.dispatchEvent( eventEnter );
|
||||
assert.htmlEqual( target.innerHTML, `
|
||||
<button>action</button>
|
||||
<div class="tooltip">Perform an Action</div>
|
||||
` );
|
||||
|
||||
button.dispatchEvent( eventLeave );
|
||||
assert.htmlEqual( target.innerHTML, `
|
||||
<button>action</button>
|
||||
` );
|
||||
}
|
||||
};
|
@ -0,0 +1,46 @@
|
||||
<button use:tooltip="t(actionTransKey)">action</button>
|
||||
|
||||
<script>
|
||||
const translations = {
|
||||
perform_action: 'Perform an Action'
|
||||
};
|
||||
|
||||
function t(key) {
|
||||
return translations[key] || `{{${key}}}`;
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { t, actionTransKey: 'perform_action' };
|
||||
},
|
||||
|
||||
actions: {
|
||||
tooltip(node, text) {
|
||||
let tooltip = null;
|
||||
|
||||
function onMouseEnter() {
|
||||
tooltip = document.createElement('div');
|
||||
tooltip.classList.add('tooltip');
|
||||
tooltip.textContent = text;
|
||||
node.parentNode.appendChild(tooltip);
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
if (!tooltip) return;
|
||||
tooltip.remove();
|
||||
tooltip = null;
|
||||
}
|
||||
|
||||
node.addEventListener('mouseenter', onMouseEnter);
|
||||
node.addEventListener('mouseleave', onMouseLeave);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
node.removeEventListener('mouseenter', onMouseEnter);
|
||||
node.removeEventListener('mouseleave', onMouseLeave);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -0,0 +1,29 @@
|
||||
export default {
|
||||
html: `
|
||||
<button>action</button>
|
||||
`,
|
||||
|
||||
test ( assert, component, target, window ) {
|
||||
const button = target.querySelector( 'button' );
|
||||
const eventEnter = new window.MouseEvent( 'mouseenter' );
|
||||
const eventLeave = new window.MouseEvent( 'mouseleave' );
|
||||
const ctrlPress = new window.KeyboardEvent( 'keydown', { ctrlKey: true } );
|
||||
|
||||
button.dispatchEvent( eventEnter );
|
||||
assert.htmlEqual( target.innerHTML, `
|
||||
<button>action</button>
|
||||
<div class="tooltip">Perform an Action</div>
|
||||
` );
|
||||
|
||||
window.dispatchEvent( ctrlPress );
|
||||
assert.htmlEqual( target.innerHTML, `
|
||||
<button>action</button>
|
||||
<div class="tooltip">Perform an augmented Action</div>
|
||||
` );
|
||||
|
||||
button.dispatchEvent( eventLeave );
|
||||
assert.htmlEqual( target.innerHTML, `
|
||||
<button>action</button>
|
||||
` );
|
||||
}
|
||||
};
|
@ -0,0 +1,52 @@
|
||||
<button use:tooltip="tooltip">action</button>
|
||||
<:Window on:keydown="checkForCtrl(event)" on:keyup="checkForCtrl(event)"/>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return { tooltip: 'Perform an Action' };
|
||||
},
|
||||
|
||||
methods: {
|
||||
checkForCtrl(event) {
|
||||
if (event.ctrlKey) {
|
||||
this.set({ tooltip: 'Perform an augmented Action' });
|
||||
} else {
|
||||
this.set({ tooltip: 'Perform an Action' });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
tooltip(node, text) {
|
||||
let tooltip = null;
|
||||
|
||||
function onMouseEnter() {
|
||||
tooltip = document.createElement('div');
|
||||
tooltip.classList.add('tooltip');
|
||||
tooltip.textContent = text;
|
||||
node.parentNode.appendChild(tooltip);
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
if (!tooltip) return;
|
||||
tooltip.remove();
|
||||
tooltip = null;
|
||||
}
|
||||
|
||||
node.addEventListener('mouseenter', onMouseEnter);
|
||||
node.addEventListener('mouseleave', onMouseLeave);
|
||||
|
||||
return {
|
||||
update(text) {
|
||||
if (tooltip) tooltip.textContent = text;
|
||||
},
|
||||
destroy() {
|
||||
node.removeEventListener('mouseenter', onMouseEnter);
|
||||
node.removeEventListener('mouseleave', onMouseLeave);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
html: `
|
||||
<button>action</button>
|
||||
`,
|
||||
|
||||
test ( assert, component, target, window ) {
|
||||
const button = target.querySelector( 'button' );
|
||||
const eventEnter = new window.MouseEvent( 'mouseenter' );
|
||||
const eventLeave = new window.MouseEvent( 'mouseleave' );
|
||||
|
||||
button.dispatchEvent( eventEnter );
|
||||
assert.htmlEqual( target.innerHTML, `
|
||||
<button>action</button>
|
||||
<div class="tooltip">Perform an Action</div>
|
||||
` );
|
||||
|
||||
button.dispatchEvent( eventLeave );
|
||||
assert.htmlEqual( target.innerHTML, `
|
||||
<button>action</button>
|
||||
` );
|
||||
}
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
<button use:tooltip="'Perform an Action'">action</button>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
actions: {
|
||||
tooltip(node, text) {
|
||||
let tooltip = null;
|
||||
|
||||
function onMouseEnter() {
|
||||
tooltip = document.createElement('div');
|
||||
tooltip.classList.add('tooltip');
|
||||
tooltip.textContent = text;
|
||||
node.parentNode.appendChild(tooltip);
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
if (!tooltip) return;
|
||||
tooltip.remove();
|
||||
tooltip = null;
|
||||
}
|
||||
|
||||
node.addEventListener('mouseenter', onMouseEnter);
|
||||
node.addEventListener('mouseleave', onMouseLeave);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
node.removeEventListener('mouseenter', onMouseEnter);
|
||||
node.removeEventListener('mouseleave', onMouseLeave);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -0,0 +1,46 @@
|
||||
<button use:tooltip="t(actionTransKey)">action</button>
|
||||
|
||||
<script>
|
||||
const translations = {
|
||||
perform_action: 'Perform an Action'
|
||||
};
|
||||
|
||||
function t(key) {
|
||||
return translations[key] || `{{${key}}}`;
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { t, actionTransKey: 'perform_action' };
|
||||
},
|
||||
|
||||
actions: {
|
||||
tooltip(node, text) {
|
||||
let tooltip = null;
|
||||
|
||||
function onMouseEnter() {
|
||||
tooltip = document.createElement('div');
|
||||
tooltip.classList.add('tooltip');
|
||||
tooltip.textContent = text;
|
||||
node.parentNode.appendChild(tooltip);
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
if (!tooltip) return;
|
||||
tooltip.remove();
|
||||
tooltip = null;
|
||||
}
|
||||
|
||||
node.addEventListener('mouseenter', onMouseEnter);
|
||||
node.addEventListener('mouseleave', onMouseLeave);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
node.removeEventListener('mouseenter', onMouseEnter);
|
||||
node.removeEventListener('mouseleave', onMouseLeave);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -0,0 +1,12 @@
|
||||
[{
|
||||
"message": "Missing action 'whatever'",
|
||||
"pos": 5,
|
||||
"loc": {
|
||||
"line": 1,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 17
|
||||
}
|
||||
}]
|
@ -0,0 +1 @@
|
||||
<div use:whatever></div>
|
@ -0,0 +1,12 @@
|
||||
[{
|
||||
"message": "Actions can only be applied to DOM elements, not components",
|
||||
"pos": 8,
|
||||
"loc": {
|
||||
"line": 1,
|
||||
"column": 8
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 15
|
||||
}
|
||||
}]
|
@ -0,0 +1,15 @@
|
||||
<Widget use:foo/>
|
||||
|
||||
<script>
|
||||
import Widget from './Widget.html';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Widget
|
||||
},
|
||||
|
||||
actions: {
|
||||
foo() {}
|
||||
}
|
||||
};
|
||||
</script>
|
Loading…
Reference in new issue