8000 add app loading splash screen and AppConfig (#279) · Linux4SA/pyscript@71319d0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 71319d0

Browse files
authored
add app loading splash screen and AppConfig (pyscript#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
1 parent 5f19756 commit 71319d0

File tree

6 files changed

+179
-1
lines changed

6 files changed

+179
-1
lines changed

pyscriptjs/examples/simple_clock.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
<div id="outputDiv2" class="font-mono"></div>
2222
<div id="outputDiv3" class="font-mono"></div>
2323
<py-script output="outputDiv">
24+
# demonstrates how use the global PyScript pyscript_loader
25+
# to send operation log messages to it
2426
import utils
2527
utils.now()
2628
</py-script>
< 10000 div class="diff-text-inner color-fg-muted">

pyscriptjs/src/App.svelte

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
<script lang="ts">
22
import Tailwind from './Tailwind.svelte';
33
import { loadInterpreter } from './interpreter';
4-
import { initializers, loadedEnvironments, mode, postInitializers, pyodideLoaded, scriptsQueue } from './stores';
4+
import type { AppConfig } from './components/pyconfig';
5+
import { initializers, loadedEnvironments, mode, postInitializers, pyodideLoaded, scriptsQueue, globalLoader, appConfig } from './stores';
56
67
let pyodideReadyPromise;
78
9+
let loader;
10+
let appConfig_: AppConfig = {
11+
autoclose_loader: true,
12+
};
13+
14+
globalLoader.subscribe(value => {
15+
loader = value;
16+
});
17+
18+
appConfig.subscribe( (value:AppConfig) => {
19+
if (value){
20+
appConfig_ = value;
21+
}
22+
console.log("config set!")
23+
});
24+
825
const initializePyodide = async () => {
26+
loader.log("Loading runtime...")
927
pyodideReadyPromise = loadInterpreter();
1028
const pyodide = await pyodideReadyPromise;
1129
let newEnv = {
@@ -16,16 +34,22 @@
1634
};
1735
pyodideLoaded.set(pyodide);
1836
37+
// Inject the loader into the runtime namespace
38+
pyodide.globals.set("pyscript_loader", loader);
39+
40+
loader.log("Runtime created...")
1941
loadedEnvironments.update((value: any): any => {
2042
value[newEnv['id']] = newEnv;
2143
});
2244
2345
// now we call all initializers before we actually executed all page scripts
46+
loader.log("Initializing components...")
2447
for (let initializer of $initializers) {
2548
await initializer();
2649
}
2750
2851
// now we can actually execute the page scripts if we are in play mode
52+
loader.log("Initializing scripts...")
2953
if ($mode == 'play') {
3054
for (let script of $scriptsQueue) {
3155
await script.evaluate();
@@ -34,6 +58,13 @@
3458
}
3559
3660
// now we call all post initializers AFTER we actually executed all page scripts
61+
loader.log("Running post initializers...");
62+
63+
if (appConfig_ && appConfig_.autoclose_loader) {
64+
loader.close();
65+
console.log("------ loader closed ------");
66+
}
67+
3768
setTimeout(async () => {
3869
for (let initializer of $postInitializers) {
3970
await initializer();
@@ -42,6 +73,38 @@
4273
};
4374
</script>
4475

76+
<style global>
77+
.spinner::after {
78+
content: '';
79+
box-sizing: border-box;
80+
width: 40px;
81+
height: 40px;
82+
position: absolute;
83+
top: calc(40% - 20px);
84+
left: calc(50% - 20px);
85+
border-radius: 50%;
86+
}
87+
88+
.spinner.smooth::after {
89+
border-top: 4px solid rgba(255, 255, 255, 1.0);
90+
border-left: 4px solid rgba(255, 255, 255, 1.0);
91+
border-right: 4px solid rgba(255, 255, 255, 0.0);
92+
animation: spinner .6s linear infinite;
93+
}
94+
@keyframes spinner {
95+
to {transform: rotate(360deg);}
96+
}
97+
98+
.label {
99+
text-align: center;
100+
width: 100%;
101+
display: block;
102+
color: rgba(255, 255, 255, 0.8);
103+
font-size: 0.8rem;
104+
margin-top: 6rem;
105+
}
106+
</style>
107+
45108
<svelte:head>
46109
<script src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js" on:load={initializePyodide}></script>
47110
</svelte:head>

pyscriptjs/src/components/pyconfig.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as jsyaml from 'js-yaml';
2+
import { BaseEvalElement } from './base';
3+
import { appConfig } from '../stores';
4+
5+
let appConfig_;
6+
7+
appConfig.subscribe(value => {
8+
appConfig_ = value;
9+
});
10+
11+
export type AppConfig = {
12+
autoclose_loader: boolean;
13+
name?: string;
14+
version?: string;
15+
};
16+
17+
export class PyConfig extends BaseEvalElement {
18+
shadow: ShadowRoot;
19+
wrapper: HTMLElement;
20+
theme: string;
21+
widths: Array<string>;
22+
label: string;
23+
mount_name: string;
24+
details: HTMLElement;
25+
operation: HTMLElement;
26+
code: string;
27+
values: AppConfig;
28+
constructor() {
29+
super();
30+
}
31+
32+
connectedCallback() {
33+
this.code = this.innerHTML;
34+
this.innerHTML = '';
35+
36+
this.values = jsyaml.load(this.code);
37+
if (this.values === undefined){
38+
this.values = {
39+
autoclose_loader: true,
40+
};
41+
}
42+
appConfig.set(this.values);
43+
console.log("config set", this.values);
44+
}
45+
46+
log(msg: string){
47+
const newLog = document.createElement('p');
48+
newLog.innerText = msg;
49+
this.details.appendChild(newLog);
50+
}
51+
52+
close() {
53+
this.remove();
54+
}
55+
}

pyscriptjs/src/components/pyloader.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { BaseEvalElement } from './base';
2+
3+
export class PyLoader extends BaseEvalElement {
4+
shadow: ShadowRoot;
5+
wrapper: HTMLElement;
6+
theme: string;
7+
widths: Array<string>;
8+
label: string;
9+
mount_name: string;
10+
details: HTMLElement;
11+
operation: HTMLElement;
12+
constructor() {
13+
super();
14+
}
15+
16+
connectedCallback() {
17+
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">
18+
<div class="smooth spinner"></div>
19+
<div id="pyscript-loading-label" class="label">
20+
<div id="pyscript-operation-details">
21+
</div>
22+
</div>
23+
</div>`;
24+
this.mount_name = this.id.split('-').join('_');
25+
this.operation = document.getElementById('pyscript-operation');
26+
this.details = document.getElementById('pyscript-operation-details');
27+
}
28+
29+
log(msg: string){
30+
const newLog = document.createElement('p');
31+
newLog.innerText = msg;
32+
this.details.appendChild(newLog);
33+
}
34+
35+
close() {
36+
this.remove();
37+
}
38+
}

pyscriptjs/src/main.ts

Lines changed 2851 : 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import { PyButton } from './components/pybutton';
88
import { PyTitle } from './components/pytitle';
99
import { PyInputBox } from './components/pyinputbox';
1010
import { PyWidget } from './components/base';
11+
import { PyLoader } from './components/pyloader';
12+
import { globalLoader } from './stores';
13+
import { PyConfig } from './components/pyconfig';
1114

1215
const xPyScript = customElements.define('py-script', PyScript);
1316
const xPyRepl = customElements.define('py-repl', PyRepl);
@@ -17,6 +20,21 @@ const xPyButton = customElements.define('py-button', PyButton);
1720
const xPyTitle = customElements.define('py-title', PyTitle);
1821
const xPyInputBox = customElements.define('py-inputbox', PyInputBox);
1922
const xPyWidget = customElements.define('py-register-widget', PyWidget);
23+
const xPyLoader = customElements.define('py-loader', PyLoader);
24+
const xPyConfig = customElements.define('py-config', PyConfig);
25+
26+
27+
// As first thing, loop for application configs
28+
const config = document.querySelector('py-config');
29+
if (!config){
30+
const loader = document.createElement('py-config');
31+
document.body.append(loader);
32+
}
33+
34+
// add loader to the page body
35+
const loader = document.createElement('py-loader');
36+
document.body.append(loader);
37+
globalLoader.set(loader);
2038

2139
const app = new App({
2240
target: document.body,

pyscriptjs/src/stores.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export const mode = writable(DEFAULT_MODE);
2020
export const scriptsQueue = writable<PyScript[]>([]);
2121
export const initializers = writable<Initializer[]>([]);
2222
export const postInitializers = writable<Initializer[]>([]);
23+
export const globalLoader = writable();
24+
export const appConfig = writable();
2325

2426
let scriptsQueue_: PyScript[] = [];
2527
let initializers_: Initializer[] = [];

0 commit comments

Comments
 (0)
0