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="outputDiv2" class="font-mono"></div>
|
||||||
<div id="outputDiv3" class="font-mono"></div>
|
<div id="outputDiv3" class="font-mono"></div>
|
||||||
<py-script output="outputDiv">
|
<py-script output="outputDiv">
|
||||||
|
# demonstrates how use the global PyScript pyscript_loader
|
||||||
|
# to send operation log messages to it
|
||||||
import utils
|
import utils
|
||||||
utils.now()
|
utils.now()
|
||||||
</py-script>
|
</py-script>
|
||||||
|
|||||||
@@ -1,11 +1,29 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Tailwind from './Tailwind.svelte';
|
import Tailwind from './Tailwind.svelte';
|
||||||
import { loadInterpreter } from './interpreter';
|
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 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 () => {
|
const initializePyodide = async () => {
|
||||||
|
loader.log("Loading runtime...")
|
||||||
pyodideReadyPromise = loadInterpreter();
|
pyodideReadyPromise = loadInterpreter();
|
||||||
const pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
let newEnv = {
|
let newEnv = {
|
||||||
@@ -16,16 +34,22 @@
|
|||||||
};
|
};
|
||||||
pyodideLoaded.set(pyodide);
|
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 => {
|
loadedEnvironments.update((value: any): any => {
|
||||||
value[newEnv['id']] = newEnv;
|
value[newEnv['id']] = newEnv;
|
||||||
});
|
});
|
||||||
|
|
||||||
// now we call all initializers before we actually executed all page scripts
|
// now we call all initializers before we actually executed all page scripts
|
||||||
|
loader.log("Initializing components...")
|
||||||
for (let initializer of $initializers) {
|
for (let initializer of $initializers) {
|
||||||
await initializer();
|
await initializer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// now we can actually execute the page scripts if we are in play mode
|
// now we can actually execute the page scripts if we are in play mode
|
||||||
|
loader.log("Initializing scripts...")
|
||||||
if ($mode == 'play') {
|
if ($mode == 'play') {
|
||||||
for (let script of $scriptsQueue) {
|
for (let script of $scriptsQueue) {
|
||||||
await script.evaluate();
|
await script.evaluate();
|
||||||
@@ -34,6 +58,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// now we call all post initializers AFTER we actually executed all page scripts
|
// 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 () => {
|
setTimeout(async () => {
|
||||||
for (let initializer of $postInitializers) {
|
for (let initializer of $postInitializers) {
|
||||||
await initializer();
|
await initializer();
|
||||||
@@ -42,6 +73,38 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</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>
|
<svelte:head>
|
||||||
<script src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js" on:load={initializePyodide}></script>
|
<script src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js" on:load={initializePyodide}></script>
|
||||||
</svelte:head>
|
</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 { PyTitle } from './components/pytitle';
|
||||||
import { PyInputBox } from './components/pyinputbox';
|
import { PyInputBox } from './components/pyinputbox';
|
||||||
import { PyWidget } from './components/base';
|
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 xPyScript = customElements.define('py-script', PyScript);
|
||||||
const xPyRepl = customElements.define('py-repl', PyRepl);
|
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 xPyTitle = customElements.define('py-title', PyTitle);
|
||||||
const xPyInputBox = customElements.define('py-inputbox', PyInputBox);
|
const xPyInputBox = customElements.define('py-inputbox', PyInputBox);
|
||||||
const xPyWidget = customElements.define('py-register-widget', PyWidget);
|
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({
|
const app = new App({
|
||||||
target: document.body,
|
target: document.body,
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export const mode = writable(DEFAULT_MODE);
|
|||||||
export const scriptsQueue = writable<PyScript[]>([]);
|
export const scriptsQueue = writable<PyScript[]>([]);
|
||||||
export const initializers = writable<Initializer[]>([]);
|
export const initializers = writable<Initializer[]>([]);
|
||||||
export const postInitializers = writable<Initializer[]>([]);
|
export const postInitializers = writable<Initializer[]>([]);
|
||||||
|
export const globalLoader = writable();
|
||||||
|
export const appConfig = writable();
|
||||||
|
|
||||||
let scriptsQueue_: PyScript[] = [];
|
let scriptsQueue_: PyScript[] = [];
|
||||||
let initializers_: Initializer[] = [];
|
let initializers_: Initializer[] = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user