mirror of
https://github.com/pyscript/pyscript.git
synced 2022-05-01 19:47:48 +03:00
add support for custom widgets registration in Python
This commit is contained in:
@@ -151,3 +151,171 @@ export class BaseEvalElement extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createWidget(name: string, code: string, klass: string){
|
||||||
|
|
||||||
|
|
||||||
|
class CustomWidget extends HTMLElement{
|
||||||
|
shadow: ShadowRoot;
|
||||||
|
wrapper: HTMLElement;
|
||||||
|
|
||||||
|
name: string = name;
|
||||||
|
klass: string = klass;
|
||||||
|
code: string = code;
|
||||||
|
proxy: any;
|
||||||
|
proxyClass: any;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
// attach shadow so we can preserve the element original innerHtml content
|
||||||
|
this.shadow = this.attachShadow({ mode: 'open'});
|
||||||
|
|
||||||
|
this.wrapper = document.createElement('slot');
|
||||||
|
this.shadow.appendChild(this.wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
console.log(this.name, 'connected!!!!')
|
||||||
|
this.eval(this.code).then(() => {
|
||||||
|
this.proxy = this.proxyClass(this);
|
||||||
|
console.log('proxy', this.proxy);
|
||||||
|
this.proxy.connect();
|
||||||
|
this.registerWidget();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async registerWidget(){
|
||||||
|
let pyodide = await pyodideReadyPromise;
|
||||||
|
|
||||||
|
console.log('new widget registered:', this.name);
|
||||||
|
|
||||||
|
|
||||||
|
pyodide.globals.set(this.id, this.proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
async eval(source: string): Promise<void> {
|
||||||
|
let output;
|
||||||
|
let pyodide = await pyodideReadyPromise;
|
||||||
|
try{
|
||||||
|
output = await pyodide.runPythonAsync(source);
|
||||||
|
this.proxyClass = pyodide.globals.get(this.klass);
|
||||||
|
if (output !== undefined){
|
||||||
|
console.log(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let xPyWidget = customElements.define(name, CustomWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PyWidget extends HTMLElement {
|
||||||
|
shadow: ShadowRoot;
|
||||||
|
name: string;
|
||||||
|
klass: string;
|
||||||
|
outputElement: HTMLElement;
|
||||||
|
errorElement: HTMLElement;
|
||||||
|
wrapper: HTMLElement;
|
||||||
|
theme: string;
|
||||||
|
source: string;
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
// attach shadow so we can preserve the element original innerHtml content
|
||||||
|
this.shadow = this.attachShadow({ mode: 'open'});
|
||||||
|
|
||||||
|
this.wrapper = document.createElement('slot');
|
||||||
|
this.shadow.appendChild(this.wrapper);
|
||||||
|
|
||||||
|
if (this.hasAttribute('src')) {
|
||||||
|
this.source = this.getAttribute('src');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasAttribute('name')) {
|
||||||
|
this.name = this.getAttribute('name');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasAttribute('klass')) {
|
||||||
|
this.klass = this.getAttribute('klass');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
if (this.id === undefined){
|
||||||
|
throw new ReferenceError(`No id specified for component. Components must have an explicit id. Please use id="" to specify your component id.`)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mainDiv = document.createElement('div');
|
||||||
|
mainDiv.id = this.id + '-main';
|
||||||
|
this.appendChild(mainDiv);
|
||||||
|
console.log('reading source')
|
||||||
|
this.getSourceFromFile(this.source).then((code:string) => {
|
||||||
|
this.code = code;
|
||||||
|
createWidget(this.name, code, this.klass);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('py-template connected');
|
||||||
|
}
|
||||||
|
|
||||||
|
initOutErr(): void {
|
||||||
|
if (this.hasAttribute('output')) {
|
||||||
|
this.errorElement = this.outputElement = document.getElementById(this.getAttribute('output'));
|
||||||
|
|
||||||
|
// in this case, the default output-mode is append, if hasn't been specified
|
||||||
|
if (!this.hasAttribute('output-mode')) {
|
||||||
|
this.setAttribute('output-mode', 'append');
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if (this.hasAttribute('std-out')){
|
||||||
|
this.outputElement = document.getElementById(this.getAttribute('std-out'));
|
||||||
|
}else{
|
||||||
|
// In this case neither output or std-out have been provided so we need
|
||||||
|
// to create a new output div to output to
|
||||||
|
this.outputElement = document.createElement('div');
|
||||||
|
this.outputElement.classList.add("output");
|
||||||
|
this.outputElement.hidden = true;
|
||||||
|
this.outputElement.id = this.id + "-" + this.getAttribute("exec-id");
|
||||||
|
|
||||||
|
// add the output div id if there's not output pre-defined
|
||||||
|
//mainDiv.appendChild(this.outputElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasAttribute('std-err')){
|
||||||
|
this.outputElement = document.getElementById(this.getAttribute('std-err'));
|
||||||
|
}else{
|
||||||
|
this.errorElement = this.outputElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSourceFromFile(s: string): Promise<string>{
|
||||||
|
let pyodide = await pyodideReadyPromise;
|
||||||
|
let response = await fetch(s);
|
||||||
|
return await response.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
async eval(source: string): Promise<void> {
|
||||||
|
let output;
|
||||||
|
let pyodide = await pyodideReadyPromise;
|
||||||
|
try{
|
||||||
|
output = await pyodide.runPythonAsync(source);
|
||||||
|
|
||||||
|
if (output !== undefined){
|
||||||
|
console.log(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ async function mountElements() {
|
|||||||
for (var el of matches) {
|
for (var el of matches) {
|
||||||
let mountName = el.getAttribute('py-mount');
|
let mountName = el.getAttribute('py-mount');
|
||||||
if (!mountName){
|
if (!mountName){
|
||||||
mountName = el.id.replace("-", "_");
|
mountName = el.id.split("-").join("_");
|
||||||
}
|
}
|
||||||
source += `\n${ mountName } = Element("${ el.id }")`;
|
source += `\n${ mountName } = Element("${ el.id }")`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,14 @@ class Element:
|
|||||||
self._element = document.querySelector(f'#{self._id}');
|
self._element = document.querySelector(f'#{self._id}');
|
||||||
return self._element
|
return self._element
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
return self.element.value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def innerHtml(self):
|
||||||
|
return self.element.innerHtml
|
||||||
|
|
||||||
def write(self, value, append=False):
|
def write(self, value, append=False):
|
||||||
console.log(f"Element.write: {value} --> {append}")
|
console.log(f"Element.write: {value} --> {append}")
|
||||||
# TODO: it should be the opposite... pyscript.write should use the Element.write
|
# TODO: it should be the opposite... pyscript.write should use the Element.write
|
||||||
@@ -96,6 +104,17 @@ class Element:
|
|||||||
|
|
||||||
return Element(clone.id, clone)
|
return Element(clone.id, clone)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_class(self, classname):
|
||||||
|
if isinstance(classname, list):
|
||||||
|
for cl in classname:
|
||||||
|
self.remove_class(cl)
|
||||||
|
else:
|
||||||
|
self.element.classList.remove(classname)
|
||||||
|
|
||||||
|
def add_class(self, classname):
|
||||||
|
self.element.classList.add(classname)
|
||||||
|
|
||||||
class OutputCtxManager:
|
class OutputCtxManager:
|
||||||
def __init__(self, out=None, output_to_console=True, append=True):
|
def __init__(self, out=None, output_to_console=True, append=True):
|
||||||
self._out = out
|
self._out = out
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import { PyScript } from "./components/pyscript";
|
|||||||
import { PyRepl } from "./components/pyrepl";
|
import { PyRepl } from "./components/pyrepl";
|
||||||
import { PyEnv } from "./components/pyenv";
|
import { PyEnv } from "./components/pyenv";
|
||||||
import { PyBox } from "./components/pybox";
|
import { PyBox } from "./components/pybox";
|
||||||
|
import { PyWidget } from "./components/base";
|
||||||
|
|
||||||
let xPyScript = customElements.define('py-script', PyScript);
|
let xPyScript = customElements.define('py-script', PyScript);
|
||||||
let xPyRepl = customElements.define('py-repl', PyRepl);
|
let xPyRepl = customElements.define('py-repl', PyRepl);
|
||||||
let xPyEnv = customElements.define('py-env', PyEnv);
|
let xPyEnv = customElements.define('py-env', PyEnv);
|
||||||
let xPyBox = customElements.define('py-box', PyBox);
|
let xPyBox = customElements.define('py-box', PyBox);
|
||||||
|
let xPyWidget = customElements.define('py-register-widget', PyWidget);
|
||||||
|
|
||||||
|
|
||||||
const app = new App({
|
const app = new App({
|
||||||
|
|||||||
Reference in New Issue
Block a user