8000 add support for custom widgets registration in Python · Nullinteger65/pyscript@a702cbd · GitHub
[go: up one dir, main page]

Skip to content

Commit a702cbd

Browse files
committed
add support for custom widgets registration in Python
1 parent e409f5c commit a702cbd

File tree

4 files changed

+190
-2
lines changed

4 files changed

+190
-2
lines changed

pyscriptjs/src/components/base.ts

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,171 @@ export class BaseEvalElement extends HTMLElement {
151151
}
152152
}
153153
}
154+
155+
function createWidget(name: string, code: string, klass: string){
156+
157+
158+
class CustomWidget extends HTMLElement{
159+
shadow: ShadowRoot;
160+
wrapper: HTMLElement;
161+
162+
name: string = name;
163+
klass: string = klass;
164+
code: string = code;
165+
proxy: any;
166+
proxyClass: any;
167+
168+
constructor() {
169+
super();
170+
171+
// attach shadow so we can preserve the element original innerHtml content
172+
this.shadow = this.attachShadow({ mode: 'open'});
173+
174+
this.wrapper = document.createElement('slot');
175+
this.shadow.appendChild(this.wrapper);
176+
}
177+
178+
connectedCallback() {
179+
console.log(this.name, 'connected!!!!')
180+
this.eval(this.code).then(() => {
181+
this.proxy = this.proxyClass(this);
182+
console.log('proxy', this.proxy);
183+
this.proxy.connect();
184+
this.registerWidget();
185+
});
186+
}
187+
188+
async registerWidget(){
189+
let pyodide = await pyodideReadyPromise;
190+
191+
console.log('new widget registered:', this.name);
192+
193+
194+
pyodide.globals.set(this.id, this.proxy);
195+
}
196+
197+
8000 async eval(source: string): Promise<void> {
198+
let output;
199+
let pyodide = await pyodideReadyPromise;
200+
try{
201+
output = await pyodide.runPythonAsync(source);
202+
this.proxyClass = pyodide.globals.get(this.klass);
203+
if (output !== undefined){
204+
console.log(output);
205+
}
206+
207+
} catch (err) {
208+
console.log(err);
209+
}
210+
}
211+
}
212+
let xPyWidget = customElements.define(name, CustomWidget);
213+
}
214+
215+
export class PyWidget extends HTMLElement {
216+
shadow: ShadowRoot;
217+
name: string;
218+
klass: string;
219+
outputElement: HTMLElement;
220+
errorElement: HTMLElement;
221+
wrapper: HTMLElement;
222+
theme: string;
223+
source: string;
224+
code: string;
225+
226+
constructor() {
227+
super();
228+
229+
// attach shadow so we can preserve the element original innerHtml content
230+
this.shadow = this.attachShadow({ mode: 'open'});
231+
232+
this.wrapper = document.createElement('slot');
233+
this.shadow.appendChild(this.wrapper);
234+
235+
if (this.hasAttribute('src')) {
236+
this.source = this.getAttribute('src');
237+
}
238+
239+
if (this.hasAttribute('name')) {
240+
this.name = this.getAttribute('name');
241+
}
242+
243+
if (this.hasAttribute('klass')) {
244+
this.klass = this.getAttribute('klass');
245+
}
246+
}
247+
248+
249+
connectedCallback() {
250+
if (this.id === undefined){
251+
throw new ReferenceError(`No id specified for component. Components must have an explicit id. Please use id="" to specify your component id.`)
252+
return;
253+
}
254+
255+
let mainDiv = document.createElement('div');
256+
mainDiv.id = this.id + '-main';
257+
this.appendChild(mainDiv);
258+
console.log('reading source')
259+
this.getSourceFromFile(this.source).then((code:string) => {
260+
this.code = code;
261+
createWidget(this.name, code, this.klass);
262+
263+
});
264+
265+
console.log('py-template connected');
266+
}
267+
268+
initOutErr(): void {
269+
if (this.hasAttribute('output')) {
270+
this.errorElement = this.outputElement = document.getElementById(this.getAttribute('output'));
271+
272+
// in this case, the default output-mode is append, if hasn't been specified
273+
if (!this.hasAttribute('output-mode')) {
274+
this.setAttribute('output-mode', 'append');
275+
}
276+
}else{
277+
if (this.hasAttribute('std-out')){
278+
this.outputElement = document.getElementById(this.getAttribute('std-out'));
279+
}else{
280+
// In this case neither output or std-out have been provided so we need
281+
// to create a new output div to output to
282+
this.outputElement = document.createElement('div');
283+
this.outputElement.classList.add("output");
284+
this.outputElement.hidden = true;
285+
this.outputElement.id = this.id + "-" + this.getAttribute("exec-id");
286+
287+
// add the output div id if there's not output pre-defined
288+
//mainDiv.appendChild(this.outputElement);
289+
}
290+
291+
if (this.hasAttribute('std-err')){
292+
this.outputElement = document.getElementById(this.getAttribute('std-err'));
293+
}else{
294+
this.errorElement = this.outputElement;
295+
}
296+
}
297+
}
298+
299+
async getSourceFromFile(s: string): Promise<string>{
300+
let pyodide = await pyodideReadyPromise;
301+
let response = await fetch(s);
302+
return await response.text();
303+
}
304+
305+
async eval(source: string): Promise<void> {
306+
let output;
307+
let pyodide = await pyodideReadyPromise;
308+
try{
309+
output = await pyodide.runPythonAsync(source);
310+
311+
if (output !== undefined){
312+
console.log(output);
313+
}
314+
315+
} catch (err) {
316+
console.log(err);
317+
}
318+
}
319+
320+
321+
}

pyscriptjs/src/components/pyscript.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ async function mountElements() {
296296
for (var el of matches) {
297297
let mountName = el.getAttribute('py-mount');
298298
if (!mountName){
299-
mountName = el.id.replace("-", "_");
299+
mountName = el.id.split("-").join("_");
300300
}
301301
source += `\n${ mountName } = Element("${ el.id }")`;
302302
}

pyscriptjs/src/interpreter.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ class Element:
5858
self._element = document.querySelector(f'#{self._id}');
5959
return self._element
6060
61+
@property
62+
def value(self):
63+
return self.element.value
64+
65+
@property
66+
def innerHtml(self):
67+
return self.element.innerHtml
68+
6169
def write(self, value, append=False):
6270
console.log(f"Element.write: {value} --> {append}")
6371
# TODO: it should be the opposite... pyscript.write should use the Element.write
@@ -96,6 +104,17 @@ class Element:
96104
97105
return Element(clone.id, clone)
98106
107+
108+
def remove_class(self, classname):
109+
if isinstance(classname, list):
110+
for cl in classname:
111+
self.remove_class(cl)
112+
else:
113+
self.element.classList.remove(classname)
114+
115+
def add_class(self, classname):
116+
self.element.classList.add(classname)
117+
99118
class OutputCtxManager:
100119
def __init__(self, out=None, output_to_console=True, append=True):
101120
self._out = out

pyscriptjs/src/main.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { PyScript } from "./components/pyscript";
44
import { PyRepl } from "./components/pyrepl";
55
import { PyEnv } from "./components/pyenv";
66
import { PyBox } from "./components/pybox";
7-
7+
import { PyWidget } from "./components/base";
88

99
let xPyScript = customElements.define('py-script', PyScript);
1010
let xPyRepl = customElements.define('py-repl', PyRepl);
1111
let xPyEnv = customElements.define('py-env', PyEnv);
1212
let xPyBox = customElements.define('py-box', PyBox);
13+
let xPyWidget = customElements.define('py-register-widget', PyWidget);
1314

1415

1516
const app = new App({

0 commit comments

Comments
 (0)
0