build/
__pycache__/
+data/
from flask import Flask, render_template, send_from_directory
+from flask_socketio import SocketIO, emit
import os
app = Flask(__name__, instance_relative_config=False)
-app.config.from_mapping(
- DATABASE=os.path.join(app.root_path, 'flaskr.sqlite')
-)
+socketio = SocketIO(app, logger=True, engineio_logger=True)
@app.route("/", methods=['get'])
def index():
@app.route("/script/<path:filename>", methods=["get"])
def script(filename):
return send_from_directory(os.path.join(app.root_path, "build"), filename)
+
+@app.route("/test")
+def test():
+ #emit("update", ["data__product", {"data":{"product":{"currentPrice":100}}}], json=True, namespace="/", broadcast=True)
+
+ return ""
--- /dev/null
+const unstablePriceChance: number = 0.7;
+
+export function renderPrice(): void {
+ const el: HTMLCollectionOf<Element> =
+ document.getElementsByClassName("price");
+
+ for (let e of el) {
+ if (e instanceof HTMLElement) {
+ if (typeof e.dataset.raw === "string") {
+ e.innerHTML = String(parseFloat(e.dataset.raw).toFixed(2));
+ }
+ }
+ }
+}
+
+export function renderUnstablePrice(): void {
+ const el: HTMLCollectionOf<Element> =
+ document.getElementsByClassName("unstablePrice");
+
+ for (let e of el) {
+ if (e instanceof HTMLElement) {
+ if (typeof e.dataset.raw === "string") {
+ if (Math.random() < unstablePriceChance) {
+ e.innerHTML = String(parseFloat(e.dataset.raw).toFixed(2));
+ } else {
+ e.innerHTML = String(parseFloat(e.dataset.raw));
+ }
+ }
+ }
+ }
+}
--- /dev/null
+export function renderText(): void {
+ const el = document.querySelectorAll(".text,.title");
+ for (let e of el) {
+ if (e instanceof HTMLElement) {
+ if (typeof e.dataset.raw === "string") {
+ e.innerHTML = e.dataset.raw;
+ }
+ }
+ }
+}
+++ /dev/null
-export default function renderTitle(): void {
- const el: HTMLCollectionOf<Element> = document.getElementsByClassName("title");
-
- for (let e of el) {
- console.log(e);
- }
-}
-import renderTitle from "./title.js";
-export {};
+import { renderText } from "./text.js";
+import { renderPrice, renderUnstablePrice } from "./price.js"
+import { io } from "socket.io-client";
-function update(): void {
- renderTitle();
+// apply a number of sequential keys to an object
+function objectPath(obj: any, path: string[]): any {
+ let key: string|undefined = path.shift();
+ if (typeof key === "undefined") {
+ return obj
+ } else {
+ return objectPath(obj[key], path)
+ }
}
-update();
+export default function setupUpdate(): void {
+ const socket = io();
+ socket.on("update", ([namespace, data]) => {
+ const el = document.querySelectorAll(`.update[class*='${namespace}']`);
+
+ for (let e of el) {
+ let c: string|undefined = Array.from(e.classList)
+ .find((c) => c.startsWith(namespace))
+
+ if (typeof c === "string") {
+ let path = c.split("__");
+ let result = objectPath(data, path);
+
+ if (typeof result !== "undefined" && e instanceof HTMLElement) {
+ e.dataset.raw = result;
+ }
+ }
+ }
+
+ renderPrice();
+ renderUnstablePrice();
+ })
+}
--- /dev/null
+title { text-transform: capitalize; }
--- /dev/null
+{#
+
+ builds the admin page
+
+#}
+<html>
+ <body>
+ <form>
+ {% include "admin/list.html" %}
+ <form>
+ </body>
+</html>
--- /dev/null
+{#
+
+ interates through the state variable and includes the relevant template for
+ each element. not tested more than double nested
+
+#}
+{% for element in state %}
+ {% if element.tag == "textarea" %}
+ {% include "admin/textarea.html" %}
+ {% elif element.tag == "select" %}
+ {% include "admin/select.html" %}
+ {% elif element.tag == "text" %}
+ {% include "admin/text.html" %}
+ {% elif element.tag == "number" %}
+ {% include "admin/number.html" %}
+ {% endif %}
+ <br>
+{% endfor %}
--- /dev/null
+<label>{{ element.name }}</label><br>
+<input type="number" name="{{ element.name }}" value="{{ element.text }}" />
--- /dev/null
+<details>
+ <summary>{{ element.name }}</summary>
+ <input type="radio" name="{{ element.name }}" value="soup" checked />
+ <div style="display: inline-block; vertical-align: middle;">
+ {% block list %}
+ {% set state = element.elements %}
+ {% include "admin/list.html" %}
+ {% endblock %}
+ </div>
+</details>
--- /dev/null
+<label>{{ element.name }}</label><br>
+<input type="text" name="{{ element.name }}" value="{{ element.text }}" />
+
--- /dev/null
+<label>{{ element.name }}</label><br>
+<textarea name="{{ element.name }}">
+ {{- element.default -}}
+</textarea>
<!doctype html>
<html>
<head>
- <script type="text/javascript" type="module" src="/script/update.js" defer></script>
+ <link rel="stylesheet" href="/static/style.css">
+ <script type="importmap">
+ {
+ "imports": {
+ "socket.io-client": "https://cdn.socket.io/4.8.3/socket.io.esm.min.js"
+ }
+ }
+ </script>
+ <script type="module" defer>
+
+import setupUpdate from "/script/update.js";
+setupUpdate();
+
+ </script>
</head>
<body>
- <span class="update root__product__current-price"></span>
+ <span class="update data__product__currentPrice unstablePrice">This is some text</span>
</body>
</html>
{
- // Visit https://aka.ms/tsconfig to read more about this file
- "include": ["./director/src"],
+ "include": ["./director/src","./director/static"],
+
"compilerOptions": {
- // File Layout
"rootDir": "./director/src",
"outDir": "./director/build",
- // Environment Settings
- // See also https://aka.ms/tsconfig/module
- "module": "nodenext",
+ "module": "esnext",
"target": "esnext",
- "types": [],
- // For nodejs:
- // "lib": ["esnext"],
- // "types": ["node"],
- // and npm install -D @types/node
-
- // Other Outputs
- "sourceMap": true,
- "declaration": true,
- "declarationMap": true,
-
- // Stricter Typechecking Options
- "noUncheckedIndexedAccess": true,
- "exactOptionalPropertyTypes": true,
-
- // Style Options
- // "noImplicitReturns": true,
- // "noImplicitOverride": true,
- // "noUnusedLocals": true,
- // "noUnusedParameters": true,
- // "noFallthroughCasesInSwitch": true,
- // "noPropertyAccessFromIndexSignature": true,
- // Recommended Options
- "strict": true,
- "jsx": "react-jsx",
- "verbatimModuleSyntax": false,
- "isolatedModules": true,
- "noUncheckedSideEffectImports": true,
- "moduleDetection": "force",
- "skipLibCheck": true,
+ "allowJs": true,
+ "checkJs": false,
+ "resolveJsonModule": true
}
}