mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 18:27:29 -05:00
add app loading splash screen and AppConfig (#279)
* add PyLoader class * create global loader during app creation time and remove it when pyscript loading operations are done * make the loader global and open/close when apps is starting. Also add concept of app config so users can set if they want to autoclose the loader of handle it themselves * add pyconfig file * auto add global config if there's no config set in the page * remove changes to simple_clock example
This commit is contained in:
@@ -21,6 +21,8 @@
|
||||
<div id="outputDiv2" class="font-mono"></div>
|
||||
<div id="outputDiv3" class="font-mono"></div>
|
||||
<py-script output="outputDiv">
|
||||
# demonstrates how use the global PyScript pyscript_loader
|
||||
# to send operation log messages to it
|
||||
import utils
|
||||
utils.now()
|
||||
</py-script>
|
||||
|
||||
@@ -1,11 +1,29 @@
|
||||
<script lang="ts">
|
||||
import Tailwind from './Tailwind.svelte';
|
||||
import { loadInterpreter } from './interpreter';
|
||||
import { initializers, loadedEnvironments, mode, postInitializers, pyodideLoaded, scriptsQueue } from './stores';
|
||||
import type { AppConfig } from './components/pyconfig';
|
||||
import { initializers, loadedEnvironments, mode, postInitializers, pyodideLoaded, scriptsQueue, globalLoader, appConfig } from './stores';
|
||||
|
||||
let pyodideReadyPromise;
|
||||
|
||||
let loader;
|
||||
let appConfig_: AppConfig = {
|
||||
autoclose_loader: true,
|
||||
};
|
||||
|
||||
globalLoader.subscribe(value => {
|
||||
loader = value;
|
||||
});
|
||||
|
||||
appConfig.subscribe( (value:AppConfig) => {
|
||||
if (value){
|
||||
appConfig_ = value;
|
||||
}
|
||||
console.log("config set!")
|
||||
});
|
||||
|
||||
const initializePyodide = async () => {
|
||||
loader.log("Loading runtime...")
|
||||
pyodideReadyPromise = loadInterpreter();
|
||||
const pyodide = await pyodideReadyPromise;
|
||||
let newEnv = {
|
||||
@@ -16,16 +34,22 @@
|
||||
};
|
||||
pyodideLoaded.set(pyodide);
|
||||
|
||||
// Inject the loader into the runtime namespace
|
||||
pyodide.globals.set("pyscript_loader", loader);
|
||||
|
||||
loader.log("Runtime created...")
|
||||
loadedEnvironments.update((value: any): any => {
|
||||
value[newEnv['id']] = newEnv;
|
||||
});
|
||||
|
||||
// now we call all initializers before we actually executed all page scripts
|
||||
loader.log("Initializing components...")
|
||||
for (let initializer of $initializers) {
|
||||
await initializer();
|
||||
}
|
||||
|
||||
// now we can actually execute the page scripts if we are in play mode
|
||||
loader.log("Initializing scripts...")
|
||||
if ($mode == 'play') {
|
||||
for (let script of $scriptsQueue) {
|
||||
await script.evaluate();
|
||||
@@ -34,6 +58,13 @@
|
||||
}
|
||||
|
||||
// now we call all post initializers AFTER we actually executed all page scripts
|
||||
loader.log("Running post initializers...");
|
||||
|
||||
if (appConfig_ && appConfig_.autoclose_loader) {
|
||||
loader.close();
|
||||
console.log("------ loader closed ------");
|
||||
}
|
||||
|
||||
setTimeout(async () => {
|
||||
for (let initializer of $postInitializers) {
|
||||
await initializer();
|
||||
@@ -42,6 +73,38 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<style global>
|
||||
.spinner::after {
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
top: calc(40% - 20px);
|
||||
left: calc(50% - 20px);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.spinner.smooth::after {
|
||||
border-top: 4px solid rgba(255, 255, 255, 1.0);
|
||||
border-left: 4px solid rgba(255, 255, 255, 1.0);
|
||||
border-right: 4px solid rgba(255, 255, 255, 0.0);
|
||||
animation: spinner .6s linear infinite;
|
||||
}
|
||||
@keyframes spinner {
|
||||
to {transform: rotate(360deg);}
|
||||
}
|
||||
|
||||
.label {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
display: block;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 0.8rem;
|
||||
margin-top: 6rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
<script src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js" on:load={initializePyodide}></script>
|
||||
</svelte:head>
|
||||
|
||||
55
pyscriptjs/src/components/pyconfig.ts
Normal file
55
pyscriptjs/src/components/pyconfig.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import * as jsyaml from 'js-yaml';
|
||||
import { BaseEvalElement } from './base';
|
||||
import { appConfig } from '../stores';
|
||||
|
||||
let appConfig_;
|
||||
|
||||
appConfig.subscribe(value => {
|
||||
appConfig_ = value;
|
||||
});
|
||||
|
||||
export type AppConfig = {
|
||||
autoclose_loader: boolean;
|
||||
name?: string;
|
||||
version?: string;
|
||||
};
|
||||
|
||||
export class PyConfig extends BaseEvalElement {
|
||||
shadow: ShadowRoot;
|
||||
wrapper: HTMLElement;
|
||||
theme: string;
|
||||
widths: Array<string>;
|
||||
label: string;
|
||||
mount_name: string;
|
||||
details: HTMLElement;
|
||||
operation: HTMLElement;
|
||||
code: string;
|
||||
values: AppConfig;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.code = this.innerHTML;
|
||||
this.innerHTML = '';
|
||||
|
||||
this.values = jsyaml.load(this.code);
|
||||
if (this.values === undefined){
|
||||
this.values = {
|
||||
autoclose_loader: true,
|
||||
};
|
||||
}
|
||||
appConfig.set(this.values);
|
||||
console.log("config set", this.values);
|
||||
}
|
||||
|
||||
log(msg: string){
|
||||
const newLog = document.createElement('p');
|
||||
newLog.innerText = msg;
|
||||
this.details.appendChild(newLog);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.remove();
|
||||
}
|
||||
}
|
||||
38
pyscriptjs/src/components/pyloader.ts
Normal file
38
pyscriptjs/src/components/pyloader.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { BaseEvalElement } from './base';
|
||||
|
||||
export class PyLoader extends BaseEvalElement {
|
||||
shadow: ShadowRoot;
|
||||
wrapper: HTMLElement;
|
||||
theme: string;
|
||||
widths: Array<string>;
|
||||
label: string;
|
||||
mount_name: string;
|
||||
details: HTMLElement;
|
||||
operation: HTMLElement;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.innerHTML = `<div id="pyscript_loading_splash" class="fixed top-0 left-0 right-0 bottom-0 w-full h-screen z-50 overflow-hidden bg-gray-600 opacity-75 flex flex-col items-center justify-center">
|
||||
<div class="smooth spinner"></div>
|
||||
<div id="pyscript-loading-label" class="label">
|
||||
<div id="pyscript-operation-details">
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
this.mount_name = this.id.split('-').join('_');
|
||||
this.operation = document.getElementById('pyscript-operation');
|
||||
this.details = document.getElementById('pyscript-operation-details');
|
||||
}
|
||||
|
||||
log(msg: string){
|
||||
const newLog = document.createElement('p');
|
||||
newLog.innerText = msg;
|
||||
this.details.appendChild(newLog);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.remove();
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@ import { PyButton } from './components/pybutton';
|
||||
import { PyTitle } from './components/pytitle';
|
||||
import { PyInputBox } from './components/pyinputbox';
|
||||
import { PyWidget } from './components/base';
|
||||
import { PyLoader } from './components/pyloader';
|
||||
import { globalLoader } from './stores';
|
||||
import { PyConfig } from './components/pyconfig';
|
||||
|
||||
const xPyScript = customElements.define('py-script', PyScript);
|
||||
const xPyRepl = customElements.define('py-repl', PyRepl);
|
||||
@@ -17,6 +20,21 @@ const xPyButton = customElements.define('py-button', PyButton);
|
||||
const xPyTitle = customElements.define('py-title', PyTitle);
|
||||
const xPyInputBox = customElements.define('py-inputbox', PyInputBox);
|
||||
const xPyWidget = customElements.define('py-register-widget', PyWidget);
|
||||
const xPyLoader = customElements.define('py-loader', PyLoader);
|
||||
const xPyConfig = customElements.define('py-config', PyConfig);
|
||||
|
||||
|
||||
// As first thing, loop for application configs
|
||||
const config = document.querySelector('py-config');
|
||||
if (!config){
|
||||
const loader = document.createElement('py-config');
|
||||
document.body.append(loader);
|
||||
}
|
||||
|
||||
// add loader to the page body
|
||||
const loader = document.createElement('py-loader');
|
||||
document.body.append(loader);
|
||||
globalLoader.set(loader);
|
||||
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
|
||||
@@ -20,6 +20,8 @@ export const mode = writable(DEFAULT_MODE);
|
||||
export const scriptsQueue = writable<PyScript[]>([]);
|
||||
export const initializers = writable<Initializer[]>([]);
|
||||
export const postInitializers = writable<Initializer[]>([]);
|
||||
export const globalLoader = writable();
|
||||
export const appConfig = writable();
|
||||
|
||||
let scriptsQueue_: PyScript[] = [];
|
||||
let initializers_: Initializer[] = [];
|
||||
|
||||
Reference in New Issue
Block a user