[next] Drop web like events (#1578)

* Use registerJSModule when available (#1573)

* Updated version to publish latest
This commit is contained in:
Andrea Giammarchi
2023-07-06 17:56:06 +02:00
committed by GitHub
parent a14e701be4
commit c6b5ce7f55
14 changed files with 40 additions and 46 deletions

View File

@@ -246,40 +246,19 @@ Please read the [XWorker](#xworker) dedicated section to know more.
## How Events Work ## How Events Work
Inspired by the current [HTML Standard](https://html.spec.whatwg.org/multipage/webappapis.html#event-handlers): The event should contain the *interpreter* or *custom type* prefix, followed by the *event* type it'd like to handle.
> the event handler is exposed through a name, which is a string that always starts with "_on_" and is followed by the name of the event for which the handler is intended.
We took a similar approach, replacing that `on` prefix with whatever *interpreter* or *custom type* is available on the page, plus a *dash* `-` to avoid clashing with standards:
```html ```html
<script type="micropython"> <script type="micropython">
def print_type(event, double): def print_type(event):
# logs "click 4" print(event.type)
print(f"{event.type} {double(2)}")
</script> </script>
<button micropython-click="print_type(event, lambda x: x * 2)"> <button micropython-click="print_type">
print type print type
</button> </button>
``` ```
If this example felt a bit verbose, be ensured custom types would work the same: Differently from *Web* inline events, there's no code evaluation at all within the attribute: it's just a globally available name that will receive the current event and nothing else.
```html
<!-- - requires py-script custom type -->
<button py-click="print(event.type)">
print type
</button>
```
What is important to understand about *events* in PyScript is that the text within the attribute is executed just like any other inline or external content is, through the very same *interpreter*, with the notably extra feature that the `event` reference is made temporarily available as *global* by *core*.
This really reflects how otherwise native Web inline events handlers work and we think it's a great feature to support ... *but*:
* if your script runs *asynchronously* the `event` might be gone on the main / UI thread and by that time any of its `event.stopPropagation()` or `event.preventDefault()` goodness will be problematic, as too late to be executed
* if your *interpreter* is *experimental*, or incapable of running *synchronous* events, the `event` reference might be less useful
- Please note that if your code runs via *XWorker*, hence in a different thread, there are different caveats and constraints to consider. Please read the [XWorker](#xworker) dedicated section to know more.
#### The type-env attribute #### The type-env attribute
@@ -299,7 +278,7 @@ Just as the `env` attribute on a `<script>` tag specifies a specific instance of
<!-- note the micropython-env value --> <!-- note the micropython-env value -->
<button <button
micropython-env="two" micropython-env="two"
micropython-click="log()" micropython-click="log"
> >
log log
</button> </button>

View File

@@ -7,6 +7,8 @@ export const run = (interpreter, code) => interpreter.runPython(clean(code));
export const runAsync = (interpreter, code) => export const runAsync = (interpreter, code) =>
interpreter.runPythonAsync(clean(code)); interpreter.runPythonAsync(clean(code));
export const getGlobal = (interpreter, name) => interpreter.globals.get(name);
export const setGlobal = (interpreter, name, value) => { export const setGlobal = (interpreter, name, value) => {
interpreter.globals.set(name, value); interpreter.globals.set(name, value);
}; };

View File

@@ -1,6 +1,7 @@
import { fetchPaths, stdio } from "./_utils.js"; import { fetchPaths, stdio } from "./_utils.js";
import { import {
run, run,
getGlobal,
setGlobal, setGlobal,
deleteGlobal, deleteGlobal,
registerJSModule, registerJSModule,
@@ -22,6 +23,7 @@ export default {
if (config.fetch) await fetchPaths(this, interpreter, config.fetch); if (config.fetch) await fetchPaths(this, interpreter, config.fetch);
return interpreter; return interpreter;
}, },
getGlobal,
setGlobal, setGlobal,
deleteGlobal, deleteGlobal,
registerJSModule, registerJSModule,

View File

@@ -2,6 +2,7 @@ import { fetchPaths, stdio } from "./_utils.js";
import { import {
run, run,
runAsync, runAsync,
getGlobal,
setGlobal, setGlobal,
deleteGlobal, deleteGlobal,
registerJSModule, registerJSModule,
@@ -31,6 +32,7 @@ export default {
} }
return interpreter; return interpreter;
}, },
getGlobal,
setGlobal, setGlobal,
deleteGlobal, deleteGlobal,
registerJSModule, registerJSModule,

View File

@@ -24,6 +24,18 @@ export default {
return interpreter; return interpreter;
}, },
registerJSModule, registerJSModule,
getGlobal(interpreter, name) {
try {
return this.run(interpreter, name);
} catch (_) {
const method = this.run(interpreter, `method(:${name})`);
return (...args) =>
method.call(
name,
...args.map((value) => interpreter.wrap(value)),
);
}
},
setGlobal(interpreter, name, value) { setGlobal(interpreter, name, value) {
const id = `__pyscript_ruby_wasm_wasi_${name}`; const id = `__pyscript_ruby_wasm_wasi_${name}`;
globalThis[id] = value; globalThis[id] = value;

View File

@@ -28,6 +28,7 @@ export default {
return interpreter; return interpreter;
}, },
registerJSModule, registerJSModule,
getGlobal: (interpreter, name) => interpreter.global.get(name),
setGlobal(interpreter, name, value) { setGlobal(interpreter, name, value) {
interpreter.global.set(name, value); interpreter.global.set(name, value);
}, },

View File

@@ -41,12 +41,8 @@ export const listener = async (event) => {
el.getAttribute(`${name}-env`) || name, el.getAttribute(`${name}-env`) || name,
); );
const handler = registry.get(name); const handler = registry.get(name);
try { const callback = handler.getGlobal(interpreter, value);
handler.setGlobal(interpreter, "event", event); callback(event);
handler.run(interpreter, value);
} finally {
handler.deleteGlobal(interpreter, "event");
}
} }
}; };

View File

@@ -1,6 +1,6 @@
{ {
"name": "@pyscript/core", "name": "@pyscript/core",
"version": "0.0.6", "version": "0.0.7",
"description": "PyScript Next core", "description": "PyScript Next core",
"main": "./cjs/index.js", "main": "./cjs/index.js",
"types": "./types/index.d.ts", "types": "./types/index.d.ts",
@@ -66,6 +66,6 @@
"coincident": "^0.8.3" "coincident": "^0.8.3"
}, },
"worker": { "worker": {
"blob": "sha256-71McgT96jsjEBqY19EQRYQYwwoZQ99VTYu60UU6i03w=" "blob": "sha256-BqPm4/IdGDQduhprGUnwdf5iumpMmkkGNsPrPZXl+mU="
} }
} }

File diff suppressed because one or more lines are too long

View File

@@ -29,14 +29,14 @@
const button = document.createElement("button"); const button = document.createElement("button");
button.textContent = "click"; button.textContent = "click";
button.setAttribute("mpy-click", "test_click(event)"); button.setAttribute("mpy-click", "test_click");
document.body.append(button); document.body.append(button);
}, },
}); });
</script> </script>
</head> </head>
<body> <body>
<mpy-script mpy-click="test_click(event)"> <mpy-script mpy-click="test_click">
def test_click(event): def test_click(event):
print(event.type) print(event.type)

View File

@@ -16,8 +16,8 @@
print(sys.version) print(sys.version)
</script> </script>
<button <button
pyodide-pointerdown="print_version(event)" pyodide-pointerdown="print_version"
pyodide-click="print_version(event)" pyodide-click="print_version"
> >
pyodide version pyodide version
</button> </button>
@@ -29,8 +29,8 @@
print(sys.version) print(sys.version)
</script> </script>
<button <button
micropython-pointerdown="print_version(event)" micropython-pointerdown="print_version"
micropython-click="print_version(event)" micropython-click="print_version"
> >
micropython version micropython version
</button> </button>

View File

@@ -15,7 +15,7 @@
} }
} }
</script> </script>
<script type="module" src="../esm/index.js"></script> <script type="module" src="../core.js"></script>
</head> </head>
<body> <body>
<script type="ruby-wasm-wasi"> <script type="ruby-wasm-wasi">
@@ -24,7 +24,7 @@
print "ruby #{ RUBY_VERSION }p#{ RUBY_PATCHLEVEL }" print "ruby #{ RUBY_VERSION }p#{ RUBY_PATCHLEVEL }"
end end
</script> </script>
<button ruby-wasm-wasi-click="print_version($event)"> <button ruby-wasm-wasi-click="print_version">
ruby-wasm-wasi version ruby-wasm-wasi version
</button> </button>
</body> </body>

View File

@@ -33,6 +33,6 @@
print(read_file('/a.py')) print(read_file('/a.py'))
end end
</script> </script>
<button wasmoon-click="print_version(event)">wasmoon version</button> <button wasmoon-click="print_version">wasmoon version</button>
</body> </body>
</html> </html>

View File

@@ -45,7 +45,7 @@
<input type="text" placeholder="loading ..." required disabled /> <input type="text" placeholder="loading ..." required disabled />
<input <input
type="submit" type="submit"
micropython-click="handle_result(event)" micropython-click="handle_result"
disabled disabled
/> />
</body> </body>