@w-lfpup/superaction 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- name: Build and Test
1
+ name: Builds
2
2
 
3
3
  on:
4
4
  push:
@@ -10,7 +10,7 @@ jobs:
10
10
  build_and_test:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
- - uses: actions/checkout@v5
13
+ - uses: actions/checkout@v6
14
14
  - uses: actions/setup-node@v4
15
15
  - name: Install
16
16
  run: npm ci
package/README.md CHANGED
@@ -1,10 +1,13 @@
1
1
  # SuperAction-js
2
2
 
3
- A hypertext extension to dispatch meaningful actions from the DOM.
3
+ A hypertext extension to dispatch meaningful actions from HTML.
4
+
5
+ [![builds](https://github.com/w-lfpup/superaction-js/actions/workflows/builds.yml/badge.svg)](https://github.com/w-lfpup/superaction-js/actions/workflows/builds.yml)
4
6
 
5
7
  ## Install
6
8
 
7
9
  Install via npm.
10
+ ,
8
11
 
9
12
  ```sh
10
13
  npm install --save-dev @w-lfpup/superaction
@@ -18,12 +21,10 @@ npm install --save-dev https://github.com/w-lfpup/superaction-js
18
21
 
19
22
  ## Setup
20
23
 
21
- Create a `SuperAction` instance dispatch action events.
24
+ Create a `SuperAction` instance to dispatch action events.
22
25
 
23
26
  The `SuperAction` instance below listens for `click` events. Event listeners are immediately `connected` to the `document`.
24
27
 
25
- This enables the DOM to declaratively send meaningful messages to Javascript-land.
26
-
27
28
  ```js
28
29
  import { SuperAction } from "superaction";
29
30
 
@@ -34,6 +35,8 @@ const _superAction = new SuperAction({
34
35
  });
35
36
  ```
36
37
 
38
+ Now the DOM can declarativey dispatch meaningful messages from HTML to Javascript-land.
39
+
37
40
  ## Declare
38
41
 
39
42
  Add an attribute with the pattern `event:=action`. The `#action` event will dispatch from the `host` element
@@ -50,7 +53,7 @@ Add an event listener to connect action events from the UI to javascript-land.
50
53
 
51
54
  ```js
52
55
  document.addEventListener("#action", (e) => {
53
- let { action, sourceEvent, formData } = e.actionParams;
56
+ let { kind, originElement, originEvent, formData } = e.action;
54
57
 
55
58
  if ("increment" === action) {
56
59
  // increment something!
@@ -60,36 +63,80 @@ document.addEventListener("#action", (e) => {
60
63
 
61
64
  Form data is available when action events originate from form elements.
62
65
 
63
- Learn more about action events [here](./action_events.md).
66
+ ## Event stacking
64
67
 
65
- ## Typescript
68
+ `Superaction-js` listens to any DOM event that bubbles. It also dispatches all actions found along the composed path of a DOM event.
66
69
 
67
- I'm not trying to pollute your globals so if you want typed `#action` events, please add the following to your app somewhere thoughtful.
70
+ Turns out that's [all UI Events](https://www.w3.org/TR/uievents/#events-uievents). Which is a lot of events!
68
71
 
69
- ```ts
70
- import type { ActionEventInterface } from "superaction";
72
+ Consider the following example:
71
73
 
72
- declare global {
73
- interface GlobalEventHandlersEventMap {
74
- ["#action"]: ActionEventInterface;
75
- }
76
- }
74
+ ```html
75
+ <body click:="A">
76
+ <div click:="B">
77
+ <button click:="C">hai :3</button>
78
+ </div>
79
+ </body>
77
80
  ```
78
81
 
79
- ## Examples
82
+ When a person clicks the button above, the order of action events is:
83
+
84
+ - Action "C"
85
+ - Action "B"
86
+ - Action "A"
87
+
88
+ ## Propagation
89
+
90
+ Action events propagate similar to DOM events. Their declarative API reflects their DOM Event counterpart:
91
+
92
+ - `event:prevent-default`
93
+ - `event:stop-propagation`
94
+ - `event:stop-immediate-propagation`
95
+
96
+ Consider the following example:
97
+
98
+ ```html
99
+ <body
100
+ click:="A"
101
+ click:stop-immediate-propagation>
102
+ <form
103
+ click:="B"
104
+ click:prevent-default>
105
+ <button
106
+ type=submit
107
+ click:="C">
108
+ UwU
109
+ </button>
110
+ <button
111
+ type=submit
112
+ click:="D"
113
+ click:stop-propagation>
114
+ ^_^
115
+ </button>
116
+ </form>
117
+ </body>
118
+ ```
119
+
120
+ So when a person clicks the buttons above, the order of actions is:
121
+
122
+ Click button C:
80
123
 
81
- Here are some examples to demonstrate how easy it is to work with `SuperAction-js`:
124
+ - Action "C" dispatched
125
+ - `preventDefault()` is called on the original `HTMLSubmitEvent`
126
+ - Action "B" dispatched
127
+ - Action propagation is stopped similar to `event.stopImmediatePropagation()`
128
+ - Action "A" does _not_ dispatch
82
129
 
83
- - a simple [counter](https://w-lfpup.github.io/superaction-js/examples/counter/)
84
- - a small [sketchpad](https://w-lfpup.github.io/superaction-js/examples/sketch/) using an offscreen canvas
130
+ Click button D:
85
131
 
86
- ## Why do this?
132
+ - Action "D" dispatched
133
+ - Action event propagation stopped similar to `event.stopPropagation()`
87
134
 
88
- `Superaction` is inspired by the [elm](https://elm-lang.org) project.
135
+ ## Why #action ?
89
136
 
90
- It turns HTML into a declarative and _explicit_ message generator and removes several layers of indirection between UI and app state.
137
+ The `#action` event name, specifically the `#`, is used to prevent cyclical event disptaches.
91
138
 
92
- `Superaction` is a straightforward way to work with vanilla web technologies and escape the JSX rabbithole.
139
+ We can't _dynamically_ add attribtues to elements that start with `#`. And in this way, some of the infinite loop risk is mitigated.
93
140
 
94
141
  ## License
95
142
 
package/dist/mod.d.ts CHANGED
@@ -1,10 +1,16 @@
1
+ declare global {
2
+ interface GlobalEventHandlersEventMap {
3
+ ["#action"]: ActionEventInterface;
4
+ }
5
+ }
1
6
  export interface ActionInterface {
2
- action: string;
7
+ kind: string;
3
8
  formData?: FormData;
4
- sourceEvent: Event;
9
+ originElement: EventTarget;
10
+ originEvent: Event;
5
11
  }
6
12
  export interface ActionEventInterface extends Event {
7
- actionParams: ActionInterface;
13
+ action: ActionInterface;
8
14
  }
9
15
  export interface SuperActionParamsInterface {
10
16
  connected?: boolean;
@@ -17,8 +23,8 @@ export interface SuperActionInterface {
17
23
  disconnect(): void;
18
24
  }
19
25
  export declare class ActionEvent extends Event implements ActionEventInterface {
20
- actionParams: ActionInterface;
21
- constructor(actionParams: ActionInterface, eventInit?: EventInit);
26
+ action: ActionInterface;
27
+ constructor(action: ActionInterface, eventInit?: EventInit);
22
28
  }
23
29
  export declare class SuperAction implements SuperActionInterface {
24
30
  #private;
package/dist/mod.js CHANGED
@@ -1,19 +1,18 @@
1
1
  export class ActionEvent extends Event {
2
- actionParams;
3
- constructor(actionParams, eventInit) {
2
+ action;
3
+ constructor(action, eventInit) {
4
4
  super("#action", eventInit);
5
- this.actionParams = actionParams;
5
+ this.action = action;
6
6
  }
7
7
  }
8
8
  export class SuperAction {
9
9
  #connected = false;
10
- #boundDispatch;
10
+ #boundDispatch = this.#dispatch.bind(this);
11
11
  #params;
12
12
  #target;
13
13
  constructor(params) {
14
14
  this.#params = { ...params };
15
15
  this.#target = params.target ?? params.host;
16
- this.#boundDispatch = this.#dispatch.bind(this);
17
16
  if (this.#params.connected)
18
17
  this.connect();
19
18
  }
@@ -27,28 +26,31 @@ export class SuperAction {
27
26
  }
28
27
  }
29
28
  disconnect() {
29
+ if (!this.#connected)
30
+ return;
31
+ this.#connected = false;
30
32
  let { host, eventNames } = this.#params;
31
33
  for (let name of eventNames) {
32
34
  host.removeEventListener(name, this.#boundDispatch);
33
35
  }
34
36
  }
35
- #dispatch(sourceEvent) {
36
- let { type, currentTarget, target } = sourceEvent;
37
+ #dispatch(originEvent) {
38
+ let { type, currentTarget, target } = originEvent;
37
39
  if (!currentTarget)
38
40
  return;
39
41
  let formData;
40
42
  if (target instanceof HTMLFormElement)
41
43
  formData = new FormData(target);
42
- for (let node of sourceEvent.composedPath()) {
44
+ for (let node of originEvent.composedPath()) {
43
45
  if (node instanceof Element) {
44
46
  if (node.hasAttribute(`${type}:prevent-default`))
45
- sourceEvent.preventDefault();
47
+ originEvent.preventDefault();
46
48
  if (node.hasAttribute(`${type}:stop-immediate-propagation`))
47
49
  return;
48
- let action = node.getAttribute(`${type}:`);
49
- if (action) {
50
+ let kind = node.getAttribute(`${type}:`);
51
+ if (kind) {
50
52
  let composed = node.hasAttribute(`${type}:composed`);
51
- let event = new ActionEvent({ action, sourceEvent, formData }, { bubbles: true, composed });
53
+ let event = new ActionEvent({ kind, originElement: node, originEvent, formData }, { bubbles: true, composed });
52
54
  this.#target.dispatchEvent(event);
53
55
  }
54
56
  if (node.hasAttribute(`${type}:stop-propagation`))
@@ -7,11 +7,11 @@ const _superAction = new SuperAction({
7
7
  const countEl = document.querySelector("[count]");
8
8
  let count = parseFloat(countEl.textContent ?? "");
9
9
  addEventListener("#action", function (e) {
10
- let { action } = e.actionParams;
11
- if ("increment" === action) {
10
+ let { kind } = e.action;
11
+ if ("increment" === kind) {
12
12
  count += 1;
13
13
  }
14
- if ("decrement" === action) {
14
+ if ("decrement" === kind) {
15
15
  count -= 1;
16
16
  }
17
17
  countEl.textContent = count.toString();
@@ -18,13 +18,13 @@ const countEl = document.querySelector("[count]")!;
18
18
  let count = parseFloat(countEl.textContent ?? "");
19
19
 
20
20
  addEventListener("#action", function (e) {
21
- let { action } = e.actionParams;
21
+ let { kind } = e.action;
22
22
 
23
- if ("increment" === action) {
23
+ if ("increment" === kind) {
24
24
  count += 1;
25
25
  }
26
26
 
27
- if ("decrement" === action) {
27
+ if ("decrement" === kind) {
28
28
  count -= 1;
29
29
  }
30
30
 
@@ -13,32 +13,32 @@ interface PenParams {
13
13
  }
14
14
 
15
15
  interface SetupCanvas {
16
- action: "setup_canvas";
16
+ kind: "setup_canvas";
17
17
  offscreenCanvas: OffscreenCanvas;
18
18
  }
19
19
 
20
20
  interface SetCanvasParams {
21
- action: "set_canvas_params";
21
+ kind: "set_canvas_params";
22
22
  params: CanvasParams;
23
23
  }
24
24
 
25
25
  interface SetColor {
26
- action: "set_color";
26
+ kind: "set_color";
27
27
  color: string;
28
28
  }
29
29
 
30
30
  interface MovePen {
31
- action: "move_pen";
31
+ kind: "move_pen";
32
32
  params: PenParams;
33
33
  }
34
34
 
35
35
  interface PressPen {
36
- action: "press_pen";
36
+ kind: "press_pen";
37
37
  params: PenParams;
38
38
  }
39
39
 
40
40
  interface LiftPen {
41
- action: "lift_pen";
41
+ kind: "lift_pen";
42
42
  params: PenParams;
43
43
  }
44
44
 
@@ -4,37 +4,31 @@ const _superAction = new SuperAction({
4
4
  connected: true,
5
5
  eventNames: ["input", "pointerdown", "pointerup", "pointermove"],
6
6
  });
7
- // Setup workers
8
7
  const worker = new Worker("worker.js", { type: "module" });
9
8
  const canvas = document.querySelector("canvas");
10
9
  const offscreenCanvas = canvas.transferControlToOffscreen();
11
10
  const resizeObserver = new ResizeObserver(sendCanvasParams);
12
11
  resizeObserver.observe(canvas);
13
- // Add reactions
14
12
  addEventListener("#action", function (e) {
15
- let { action, sourceEvent } = e.actionParams;
16
- // send actions to the offscreen canvas worker
17
- // set color action needs input value
18
- if ("set_color" === action &&
19
- sourceEvent.target instanceof HTMLInputElement) {
13
+ let { kind, originEvent } = e.action;
14
+ if ("set_color" === kind &&
15
+ originEvent.target instanceof HTMLInputElement) {
20
16
  worker.postMessage({
21
- action,
22
- color: sourceEvent.target.value,
17
+ kind,
18
+ color: originEvent.target.value,
23
19
  });
24
20
  }
25
- // other pointer actions
26
- if (sourceEvent instanceof PointerEvent) {
27
- let { x, y, movementX, movementY } = sourceEvent;
21
+ if (originEvent instanceof PointerEvent) {
22
+ let { x, y, movementX, movementY } = originEvent;
28
23
  worker.postMessage({
29
- action,
24
+ kind,
30
25
  params: { x, y, movementX, movementY },
31
26
  });
32
27
  }
33
28
  });
34
- // Initialize offscreen canvas
35
29
  function setupCanvas() {
36
30
  worker.postMessage({
37
- action: "setup_canvas",
31
+ kind: "setup_canvas",
38
32
  offscreenCanvas,
39
33
  }, [offscreenCanvas]);
40
34
  }
@@ -42,7 +36,7 @@ function sendCanvasParams() {
42
36
  let { top, left } = canvas.getBoundingClientRect();
43
37
  let { clientWidth, clientHeight } = canvas;
44
38
  worker.postMessage({
45
- action: "set_canvas_params",
39
+ kind: "set_canvas_params",
46
40
  params: { top, left, width: clientWidth, height: clientHeight },
47
41
  });
48
42
  }
@@ -21,29 +21,27 @@ const offscreenCanvas = canvas.transferControlToOffscreen();
21
21
  const resizeObserver = new ResizeObserver(sendCanvasParams);
22
22
  resizeObserver.observe(canvas);
23
23
 
24
- // Add reactions
24
+ // send actions to the offscreen canvas worker
25
25
  addEventListener("#action", function (e: ActionEventInterface) {
26
- let { action, sourceEvent } = e.actionParams;
27
-
28
- // send actions to the offscreen canvas worker
26
+ let { kind, originEvent } = e.action;
29
27
 
30
28
  // set color action needs input value
31
29
  if (
32
- "set_color" === action &&
33
- sourceEvent.target instanceof HTMLInputElement
30
+ "set_color" === kind &&
31
+ originEvent.target instanceof HTMLInputElement
34
32
  ) {
35
33
  worker.postMessage({
36
- action,
37
- color: sourceEvent.target.value,
34
+ kind,
35
+ color: originEvent.target.value,
38
36
  });
39
37
  }
40
38
 
41
39
  // other pointer actions
42
- if (sourceEvent instanceof PointerEvent) {
43
- let { x, y, movementX, movementY } = sourceEvent;
40
+ if (originEvent instanceof PointerEvent) {
41
+ let { x, y, movementX, movementY } = originEvent;
44
42
 
45
43
  worker.postMessage({
46
- action,
44
+ kind,
47
45
  params: { x, y, movementX, movementY },
48
46
  });
49
47
  }
@@ -53,7 +51,7 @@ addEventListener("#action", function (e: ActionEventInterface) {
53
51
  function setupCanvas() {
54
52
  worker.postMessage(
55
53
  {
56
- action: "setup_canvas",
54
+ kind: "setup_canvas",
57
55
  offscreenCanvas,
58
56
  },
59
57
  [offscreenCanvas],
@@ -65,7 +63,7 @@ function sendCanvasParams() {
65
63
  let { clientWidth, clientHeight } = canvas;
66
64
 
67
65
  worker.postMessage({
68
- action: "set_canvas_params",
66
+ kind: "set_canvas_params",
69
67
  params: { top, left, width: clientWidth, height: clientHeight },
70
68
  });
71
69
  }
@@ -4,11 +4,11 @@ let pen_to_paper = false;
4
4
  let canvasParams;
5
5
  self.addEventListener("message", function (e) {
6
6
  let { data } = e;
7
- if ("setup_canvas" === data.action) {
7
+ if ("setup_canvas" === data.kind) {
8
8
  canvas = data.offscreenCanvas;
9
9
  ctx = canvas.getContext("2d");
10
10
  }
11
- if ("set_canvas_params" === data.action) {
11
+ if ("set_canvas_params" === data.kind) {
12
12
  canvas.width = data.params.width;
13
13
  canvas.height = data.params.height;
14
14
  canvasParams = data.params;
@@ -17,17 +17,16 @@ self.addEventListener("message", function (e) {
17
17
  ctx.lineCap = "round";
18
18
  }
19
19
  }
20
- if ("set_color" === data.action) {
20
+ if ("set_color" === data.kind) {
21
21
  let { color } = data;
22
22
  if (ctx) {
23
23
  ctx.strokeStyle = color;
24
24
  ctx.fillStyle = color;
25
25
  }
26
26
  }
27
- if ("press_pen" === data.action) {
27
+ if ("press_pen" === data.kind) {
28
28
  pen_to_paper = true;
29
29
  if (ctx) {
30
- // create first point
31
30
  ctx.beginPath();
32
31
  let { top, left } = canvasParams;
33
32
  let { x, y } = data.params;
@@ -36,11 +35,10 @@ self.addEventListener("message", function (e) {
36
35
  ctx.arc(dx, dy, ctx.lineWidth * 0.5, 0, 2 * Math.PI, true);
37
36
  ctx.fill();
38
37
  ctx.closePath();
39
- // start a "line"
40
38
  ctx.beginPath();
41
39
  }
42
40
  }
43
- if ("move_pen" === data.action) {
41
+ if ("move_pen" === data.kind) {
44
42
  if (ctx && pen_to_paper) {
45
43
  let { top, left } = canvasParams;
46
44
  let { movementY, movementX, x, y } = data.params;
@@ -51,7 +49,7 @@ self.addEventListener("message", function (e) {
51
49
  ctx.stroke();
52
50
  }
53
51
  }
54
- if ("lift_pen" === data.action) {
52
+ if ("lift_pen" === data.kind) {
55
53
  pen_to_paper = false;
56
54
  ctx?.closePath();
57
55
  }
@@ -8,12 +8,12 @@ let canvasParams: CanvasParams;
8
8
  self.addEventListener("message", function (e: MessageEvent<Actions>) {
9
9
  let { data } = e;
10
10
 
11
- if ("setup_canvas" === data.action) {
11
+ if ("setup_canvas" === data.kind) {
12
12
  canvas = data.offscreenCanvas;
13
13
  ctx = canvas.getContext("2d");
14
14
  }
15
15
 
16
- if ("set_canvas_params" === data.action) {
16
+ if ("set_canvas_params" === data.kind) {
17
17
  canvas.width = data.params.width;
18
18
  canvas.height = data.params.height;
19
19
  canvasParams = data.params;
@@ -23,7 +23,7 @@ self.addEventListener("message", function (e: MessageEvent<Actions>) {
23
23
  }
24
24
  }
25
25
 
26
- if ("set_color" === data.action) {
26
+ if ("set_color" === data.kind) {
27
27
  let { color } = data;
28
28
  if (ctx) {
29
29
  ctx.strokeStyle = color;
@@ -31,7 +31,7 @@ self.addEventListener("message", function (e: MessageEvent<Actions>) {
31
31
  }
32
32
  }
33
33
 
34
- if ("press_pen" === data.action) {
34
+ if ("press_pen" === data.kind) {
35
35
  pen_to_paper = true;
36
36
  if (ctx) {
37
37
  // create first point
@@ -51,7 +51,7 @@ self.addEventListener("message", function (e: MessageEvent<Actions>) {
51
51
  }
52
52
  }
53
53
 
54
- if ("move_pen" === data.action) {
54
+ if ("move_pen" === data.kind) {
55
55
  if (ctx && pen_to_paper) {
56
56
  let { top, left } = canvasParams;
57
57
  let { movementY, movementX, x, y } = data.params;
@@ -65,7 +65,7 @@ self.addEventListener("message", function (e: MessageEvent<Actions>) {
65
65
  }
66
66
  }
67
67
 
68
- if ("lift_pen" === data.action) {
68
+ if ("lift_pen" === data.kind) {
69
69
  pen_to_paper = false;
70
70
  ctx?.closePath();
71
71
  }
@@ -2,6 +2,7 @@
2
2
  "extends": "../tsconfig.json",
3
3
  "compilerOptions": {
4
4
  "declaration": false,
5
+ "removeComments": true,
5
6
  "paths": {
6
7
  "superaction": ["../dist/mod.d.ts"]
7
8
  }
package/package.json CHANGED
@@ -2,11 +2,11 @@
2
2
  "name": "@w-lfpup/superaction",
3
3
  "type": "module",
4
4
  "main": "dist/mod.js",
5
- "description": "A hypertext extension to dispatch meaningful actions from the DOM",
5
+ "description": "A hypertext extension to dispatch meaningful actions from UI events",
6
6
  "license": "BSD-3-Clause",
7
- "version": "0.1.0",
7
+ "version": "0.2.0",
8
8
  "scripts": {
9
- "prepare": "npm run build && npm run build:examples",
9
+ "prepare": "npm run build",
10
10
  "build": "npx tsc --project ./src",
11
11
  "build:examples": "npx tsc --project ./examples",
12
12
  "format": "npx prettier ./ --write"
package/src/mod.ts CHANGED
@@ -1,11 +1,18 @@
1
+ declare global {
2
+ interface GlobalEventHandlersEventMap {
3
+ ["#action"]: ActionEventInterface;
4
+ }
5
+ }
6
+
1
7
  export interface ActionInterface {
2
- action: string;
8
+ kind: string;
3
9
  formData?: FormData;
4
- sourceEvent: Event;
10
+ originElement: EventTarget;
11
+ originEvent: Event;
5
12
  }
6
13
 
7
14
  export interface ActionEventInterface extends Event {
8
- actionParams: ActionInterface;
15
+ action: ActionInterface;
9
16
  }
10
17
 
11
18
  export interface SuperActionParamsInterface {
@@ -21,25 +28,24 @@ export interface SuperActionInterface {
21
28
  }
22
29
 
23
30
  export class ActionEvent extends Event implements ActionEventInterface {
24
- actionParams: ActionInterface;
31
+ action: ActionInterface;
25
32
 
26
- constructor(actionParams: ActionInterface, eventInit?: EventInit) {
33
+ constructor(action: ActionInterface, eventInit?: EventInit) {
27
34
  super("#action", eventInit);
28
- this.actionParams = actionParams;
35
+ this.action = action;
29
36
  }
30
37
  }
31
38
 
32
39
  export class SuperAction implements SuperActionInterface {
33
40
  #connected = false;
41
+ #boundDispatch = this.#dispatch.bind(this);
34
42
 
35
- #boundDispatch: EventListenerOrEventListenerObject;
36
43
  #params: SuperActionParamsInterface;
37
44
  #target: EventTarget;
38
45
 
39
46
  constructor(params: SuperActionParamsInterface) {
40
47
  this.#params = { ...params };
41
48
  this.#target = params.target ?? params.host;
42
- this.#boundDispatch = this.#dispatch.bind(this);
43
49
 
44
50
  if (this.#params.connected) this.connect();
45
51
  }
@@ -55,32 +61,35 @@ export class SuperAction implements SuperActionInterface {
55
61
  }
56
62
 
57
63
  disconnect() {
64
+ if (!this.#connected) return;
65
+ this.#connected = false;
66
+
58
67
  let { host, eventNames } = this.#params;
59
68
  for (let name of eventNames) {
60
69
  host.removeEventListener(name, this.#boundDispatch);
61
70
  }
62
71
  }
63
72
 
64
- #dispatch(sourceEvent: Event) {
65
- let { type, currentTarget, target } = sourceEvent;
73
+ #dispatch(originEvent: Event) {
74
+ let { type, currentTarget, target } = originEvent;
66
75
  if (!currentTarget) return;
67
76
 
68
77
  let formData: FormData | undefined;
69
78
  if (target instanceof HTMLFormElement) formData = new FormData(target);
70
79
 
71
- for (let node of sourceEvent.composedPath()) {
80
+ for (let node of originEvent.composedPath()) {
72
81
  if (node instanceof Element) {
73
82
  if (node.hasAttribute(`${type}:prevent-default`))
74
- sourceEvent.preventDefault();
83
+ originEvent.preventDefault();
75
84
 
76
85
  if (node.hasAttribute(`${type}:stop-immediate-propagation`))
77
86
  return;
78
87
 
79
- let action = node.getAttribute(`${type}:`);
80
- if (action) {
88
+ let kind = node.getAttribute(`${type}:`);
89
+ if (kind) {
81
90
  let composed = node.hasAttribute(`${type}:composed`);
82
91
  let event = new ActionEvent(
83
- { action, sourceEvent, formData },
92
+ { kind, originElement: node, originEvent, formData },
84
93
  { bubbles: true, composed },
85
94
  );
86
95
 
package/action_events.md DELETED
@@ -1,76 +0,0 @@
1
- # Action Events
2
-
3
- ## Event stacking
4
-
5
- `Superaction-js` listens to any DOM event that bubbles. It also dispatches all actions found along the composed path of a DOM event.
6
-
7
- Turns out that's [all UI Events](https://www.w3.org/TR/uievents/#events-uievents). Which is a lot of events!
8
-
9
- Consider the following example:
10
-
11
- ```html
12
- <body click:="A">
13
- <div click:="B">
14
- <button click:="C">hai :3</button>
15
- </div>
16
- </body>
17
- ```
18
-
19
- When a person clicks the button above, the order of action events is:
20
-
21
- - Action "C"
22
- - Action "B"
23
- - Action "A"
24
-
25
- ## Propagation
26
-
27
- Action events propagate similar to DOM events. Their declarative API reflects their DOM Event counterpart:
28
-
29
- - `event:prevent-default`
30
- - `event:stop-propagation`
31
- - `event:stop-immediate-propagation`
32
-
33
- Consider the following example:
34
-
35
- ```html
36
- <body
37
- click:="A"
38
- click:stop-immediate-propagation>
39
- <form
40
- click:="B"
41
- click:prevent-default>
42
- <button
43
- type=submit
44
- click:="C">
45
- UwU
46
- </button>
47
- <button
48
- type=submit
49
- click:="D"
50
- click:stop-propagation>
51
- ^_^
52
- </button>
53
- </form>
54
- </body>
55
- ```
56
-
57
- So when a person clicks the buttons above, the order of actions is:
58
-
59
- Click button C:
60
-
61
- - Action "C" dispatched
62
- - `preventDefault()` is called on the original `HTMLSubmitEvent`
63
- - Action "B" dispatched
64
- - Action propagation is stopped similar to `event.stopImmediatePropagation()`
65
- - Action "A" does _not_ dispatch
66
-
67
- Click button D:
68
-
69
- - Action "D" dispatched
70
- - Action event propagation stopped similar to `event.stopPropagation()`
71
-
72
- ## Why #action ?
73
-
74
- The `#action` event name, specifically the `#`, is used to prevent cyclical event disptaches.
75
-
76
- We can't _dynamically_ add attribtues to elements that start with `#`. And in this way, some of the infinite loop risk is mitigated.