From ce550adef65a7e04c381b11c24f07a2ae1c25783 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 5 Aug 2021 00:19:13 +0800 Subject: [PATCH] [feat] allow use:actions on (#6608) --- site/content/docs/02-template-syntax.md | 4 ++- src/compiler/compile/nodes/Body.ts | 14 ++++---- .../compile/render_dom/wrappers/Body.ts | 2 ++ .../render_dom/wrappers/shared/add_actions.ts | 5 +-- src/compiler/interfaces.ts | 20 ++++++++++++ test/runtime/samples/action-body/_config.js | 18 +++++++++++ test/runtime/samples/action-body/main.svelte | 32 +++++++++++++++++++ 7 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 test/runtime/samples/action-body/_config.js create mode 100644 test/runtime/samples/action-body/main.svelte diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index 347a756274..0d96b5bcc3 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -1586,7 +1586,9 @@ All except `scrollX` and `scrollY` are readonly. --- -As with ``, this element allows you to add listeners to events on `document.body`, such as `mouseenter` and `mouseleave` which don't fire on `window`; and it has to appear at the top level of your component. +Similarly to ``, this element allows you to add listeners to events on `document.body`, such as `mouseenter` and `mouseleave`, which don't fire on `window`. It also lets you use [actions](docs#use_action) on the `` element. + +`` also has to appear at the top level of your component. ```sv { + info.attributes.forEach((node) => { if (node.type === 'EventHandler') { this.handlers.push(new EventHandler(component, this, scope, node)); + } else if (node.type === 'Action') { + this.actions.push(new Action(component, this, scope, node)); } else { // TODO there shouldn't be anything else here... } diff --git a/src/compiler/compile/render_dom/wrappers/Body.ts b/src/compiler/compile/render_dom/wrappers/Body.ts index d80ef6c4a0..0e82fca79f 100644 --- a/src/compiler/compile/render_dom/wrappers/Body.ts +++ b/src/compiler/compile/render_dom/wrappers/Body.ts @@ -7,6 +7,7 @@ import EventHandler from './Element/EventHandler'; import add_event_handlers from './shared/add_event_handlers'; import { TemplateNode } from '../../../interfaces'; import Renderer from '../Renderer'; +import add_actions from './shared/add_actions'; export default class BodyWrapper extends Wrapper { node: Body; @@ -19,5 +20,6 @@ export default class BodyWrapper extends Wrapper { render(block: Block, _parent_node: Identifier, _parent_nodes: Identifier) { add_event_handlers(block, x`@_document.body`, this.handlers); + add_actions(block, x`@_document.body`, this.node.actions); } } diff --git a/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts b/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts index b57820beb3..6429f18928 100644 --- a/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts +++ b/src/compiler/compile/render_dom/wrappers/shared/add_actions.ts @@ -1,17 +1,18 @@ import { b, x } from 'code-red'; import Block from '../../Block'; import Action from '../../../nodes/Action'; +import { Expression } from 'estree'; import is_contextual from '../../../nodes/shared/is_contextual'; export default function add_actions( block: Block, - target: string, + target: string | Expression, actions: Action[] ) { actions.forEach(action => add_action(block, target, action)); } -export function add_action(block: Block, target: string, action: Action) { +export function add_action(block: Block, target: string | Expression, action: Action) { const { expression, template_scope } = action; let snippet; let dependencies; diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index 73be74bef9..7446c4c14b 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -46,6 +46,23 @@ interface BaseDirective extends BaseNode { modifiers: string[]; } +export interface Element extends BaseNode { + type: 'InlineComponent' | 'SlotTemplate' | 'Title' | 'Slot' | 'Element' | 'Head' | 'Options' | 'Window' | 'Body'; + attributes: Array; + name: string; +} + +export interface Attribute extends BaseNode { + type: 'Attribute'; + name: string; + value: any[]; +} + +export interface SpreadAttribute extends BaseNode { + type: 'Spread'; + expression: Node; +} + export interface Transition extends BaseDirective { type: 'Transition'; intro: boolean; @@ -57,6 +74,9 @@ export type Directive = BaseDirective | Transition; export type TemplateNode = Text | MustacheTag | BaseNode +| Element +| Attribute +| SpreadAttribute | Directive | Transition | Comment; diff --git a/test/runtime/samples/action-body/_config.js b/test/runtime/samples/action-body/_config.js new file mode 100644 index 0000000000..c882499e0e --- /dev/null +++ b/test/runtime/samples/action-body/_config.js @@ -0,0 +1,18 @@ +export default { + html: '
', + + async test({ assert, target, window }) { + const enter = new window.MouseEvent('mouseenter'); + const leave = new window.MouseEvent('mouseleave'); + + await window.document.body.dispatchEvent(enter); + assert.htmlEqual(target.innerHTML, ` +
+
Perform an Action
+
+ `); + + await window.document.body.dispatchEvent(leave); + assert.htmlEqual(target.innerHTML, '
'); + } +}; diff --git a/test/runtime/samples/action-body/main.svelte b/test/runtime/samples/action-body/main.svelte new file mode 100644 index 0000000000..dd57116577 --- /dev/null +++ b/test/runtime/samples/action-body/main.svelte @@ -0,0 +1,32 @@ + + + +
\ No newline at end of file