Эх сурвалжийг харах

Continue working on project.

Cixo Develop 8 сар өмнө
parent
commit
7bc8b727b1

+ 41 - 9
make.py

@@ -37,6 +37,7 @@ templates = source / pathlib.Path("./templates/")
 app_view_file = templates / pathlib.Path("view.html")
 script_loader_file = script_source / pathlib.Path("loader.js")
 sass_loader_file = sass_source / pathlib.Path("loader.sass")
+sass_loading_screen_file = sass_source / pathlib.Path("loading-screen.sass")
 config = root / pathlib.Path("config.json")
 
 # Build dir
@@ -46,6 +47,8 @@ build = root / pathlib.Path("./build/")
 app_view_result_file = build / pathlib.Path("index.html")
 script_loader_result_file = build / pathlib.Path("bundle.js")
 sass_loader_result_file = build / pathlib.Path("bundle.css")
+sass_loading_screen_tmp_file = build / pathlib.Path("loading-screen.css")
+static_files_output = build / pathlib.Path("static")
 
 # Checking that directories exists
 if not source.is_dir():
@@ -88,6 +91,11 @@ if not sass_loader_file.is_file():
     print("SASS loader file not exists.")
     exit(-1)
 
+# Check that SASS loading screen file exists
+if not sass_loading_screen_file.is_file():
+    print("SASS loading screen file not exists.")
+    exit(-1)
+
 # Prepare result directory
 if build.is_dir():
     shutil.rmtree(build)
@@ -153,22 +161,18 @@ def compile(*command: list) -> None:
 
 # Rendering app view
 from source.make.render import render
-from source.make.dom import link, script
+from source.make.dom import link, script, style
 
 app_view_render = render(app_view_file)
 
 # App view use HTML description as replace tags
-app_view_render.start_tag = "<!--"
-app_view_render.stop_tag = "-->"
+app_view_render.start_tag = "{{"
+app_view_render.stop_tag = "}}"
 
 view_params = get_config_section("view-params")
 
 # Replace all elements from view-params config section
 for param in view_params.keys():
-    if param == "bundle_items":
-        print("Can not use param \"bundle_items\", it is reserved.")
-        exit(-3)
-
     content = view_params[param]
 
     if type(content) is str:
@@ -182,6 +186,23 @@ for param in view_params.keys():
     print("Param " + param + " not contain string or number.")
     exit(-3)
 
+# Compile loading screen sass file
+compile(
+    "sass",
+    "--sourcemap=none",
+    "-t compressed",
+    str(sass_loading_screen_file),
+    str(sass_loading_screen_tmp_file)
+)
+
+# Open compiled tmp file, add it to view, and drop tmp file
+loading_screen = style()
+
+with sass_loading_screen_tmp_file.open() as handle:
+    loading_screen.content = handle.read()
+
+sass_loading_screen_tmp_file.unlink()
+
 # Generating tags
 sass_link = link()
 sass_link.rel = "stylesheet"
@@ -191,7 +212,11 @@ sass_link.href = "./" + sass_loader_result_file.name + "?version=" + release
 js_script = script()
 js_script.src = "./" + script_loader_result_file.name + "?version=" + release
 
-bundle_items = sass_link.render() + js_script.render()
+bundle_items = (
+    sass_link.render() + 
+    js_script.render() + 
+    loading_screen.render()
+)
 
 # Add bundle items tags
 app_view_render.add("bundle_items", bundle_items)
@@ -212,7 +237,6 @@ compile(
     "esbuild",
     str(script_loader_file),
     "--bundle",
-    "--minify",
     "--outfile=" + str(script_loader_result_file)
 )
 
@@ -225,4 +249,12 @@ compile(
     str(sass_loader_result_file)
 )
 
+# Copy static folder
+compile(
+    "cp",
+    "-r",
+    str(static_files),
+    str(static_files_output)
+)
+
 print("Build success!")

+ 20 - 2
source/make/dom.py

@@ -139,20 +139,38 @@ class tag:
             attributes = attributes + " " + attribute + "=\""
             attributes = attributes + content + "\""
 
-        tag = "<" + self.name + attributes + ">"
+        tag = "<" + self.name + attributes + ">\n"
 
         if not self.twice:
             return tag
 
         closing_tag = "</" + self.name + ">"
 
-        return tag + self.content + closing_tag
+        return tag + self.content + "\n" + closing_tag + "\n"
 
     def __str__(self) -> str:
         ''' This function return tag rendered to string. '''
         
         return self.render()
 
+class style(tag):
+    '''
+    This class is responsible for style.
+    '''
+
+    def __init__(self, content: str | None = None):
+        '''
+        This create new style tag, with optional setuped content.
+
+        Parameters:
+            content (str | None): Stylesheet of the style tag
+        '''
+
+        super().__init__("style", True)
+
+        if content is not None:
+            self.content = content
+
 class script(tag):
     ''' 
     This class is responsible for script loading.

+ 54 - 1
source/scripts/assets/cx-ui.js

@@ -1 +1,54 @@
-export const cx_ui = "UwU";
+export class cx_ui {
+    static #generator(type, manipulator = null) {
+        return (name, click = null, manipulate = null) => {
+            const item = document.createElement(type);
+           
+            item.id = `${type}-${name}`;
+            item.classList.add(name);
+            item.classList.add(`${type}-${name}`);
+
+            if (manipulator !== null) {
+                manipulator(item);
+            }
+            
+            if (click !== null) {
+                item.addEventListener("click", click);
+            }
+
+            if (manipulate !== null) {
+                manipulate(item);
+            }
+
+            return item;
+        };
+    }
+
+    static get div() {
+        return this.#generator("div");
+    }
+
+    static get push() {
+        return this.#generator("input", (item) => {
+            item.type = "button";
+            item.classList.add("push");
+        });
+    }
+
+    static p(name, text, click = null, manipulate = null) {
+        const generator = this.#generator("p", (p) => { p.innerText = text; });
+        const result = generator(name, click, manipulate);
+
+        return result;
+    }
+
+    static get input() {
+        return this.#generator("input");
+    }
+
+    static image(name, url, click = null, manipulate = null) {
+        const generator = this.#generator("img", (img) => { img.src = url; });
+        const result = generator(name, click, manipulate);
+     
+        return result;
+    }
+}

+ 33 - 0
source/scripts/assets/dictionary.js

@@ -0,0 +1,33 @@
+import { language_pl_pl } from "../languages/pl_pl";
+
+export class dictionary {
+    static #instance;
+
+    constructor() {
+        if (dictionary.#instance) {
+            return dictionary.#instance;
+        }
+
+        dictionary.#instance = this;
+        return new dictionary();
+    }
+
+    get #dict() {
+        return language_pl_pl;
+    }
+
+    get(name) {
+        const splited = name.split(".");
+        let dict = this.#dict;
+       
+        for (let count in splited) {
+            if (!(splited[count] in dict)) {
+                return name;
+            }
+
+            dict = dict[splited[count]];
+        }
+
+        return dict;
+    }
+}

+ 40 - 0
source/scripts/assets/loading-screen.js

@@ -0,0 +1,40 @@
+export class loading_screen {
+    static #instance;
+    
+    #screen;
+
+    constructor(classname = "loading-screen") {
+        if (!loading_screen.#instance) {
+            loading_screen.#instance = this;
+            this.#init(classname);
+        }
+
+        return loading_screen.#instance;
+    }
+
+    get time() {
+        return 1000;
+    }
+
+    #init(classname) {
+        this.#screen = document.querySelector(`.${classname}`);
+        this.#screen.style.transitionDuration = `${this.time / 1000}s`;
+        this.show();
+    }
+
+    get visible() {
+        return !this.#screen.classList.contains("hidden");
+    }
+
+    show() {
+        if (!this.visible) {
+            this.#screen.classList.remove("hidden");
+        }
+    }
+
+    hide() {
+        if (this.visible) {
+            this.#screen.classList.add("hidden");
+        }
+    }
+}

+ 60 - 0
source/scripts/assets/screen.js

@@ -0,0 +1,60 @@
+import { view } from "./view.js";
+import { loading_screen } from "./loading-screen.js";
+
+export class screen {
+    #root;
+    #view;
+    
+    constructor(root) {
+        this.#root = root;
+    }
+
+    get root() {
+        return this.#root;
+    }
+
+    get view() {
+        return this.#view;
+    }
+
+    get #loading() {
+        return new loading_screen();
+    }
+
+    #change_view(target) {
+        if (this.#view !== undefined) {
+            this.#view.destroy();
+        }
+
+        this.#view = target;
+        const content = this.#view.show();
+
+        if (!(content instanceof HTMLElement)) {
+            throw new TypeError("View show not return HTML Element.");
+        }
+
+        while (this.root.lastChild) {
+            this.root.lastChild.remove();
+        }
+
+        this.root.appendChild(content);
+        this.#loading.hide();
+    }
+
+    set view(target) {
+        if (!(target instanceof view)) {
+            throw new Error("Target must be instance of view.");
+        }
+
+        if (this.#loading.visible) {
+            this.#change_view(target)
+            return;
+        }
+
+        this.#loading.show();
+
+        setTimeout(() => {
+            this.#change_view(target);
+        }, this.#loading.time);
+    }
+}

+ 55 - 0
source/scripts/assets/selector.js

@@ -0,0 +1,55 @@
+import { view } from "./view.js";
+import { cx_ui } from "./cx-ui.js";
+import { dictionary } from "./dictionary.js";
+import { shelf } from "./shelf.js";
+import { space } from "./space.js";
+
+export class selector extends view {
+    #manager;
+
+    #shelf() {
+        this.#manager.view = new shelf(this.#manager);
+    }
+
+    #space() {
+        this.#manager.view = new space(this.#manager);
+    }
+
+    constructor(manager) {
+        super();
+        this.#manager = manager;
+    }
+
+    show() {
+        const lang = new dictionary();
+        const container = cx_ui.div("selector");
+        const shelf = cx_ui.div("shelf");
+        const space = cx_ui.div("space");
+
+        const shelf_icon = cx_ui.image(
+            "shelf-image", 
+            "./static/icons/shelf.svg", 
+            () => { this.#shelf(); }
+        );
+
+        const space_icon = cx_ui.image(
+            "space-icon",
+            "./static/icons/space.svg",
+            () => { this.#space(); }
+        );
+
+        const shelf_title = cx_ui.p("shelf-title", lang.get("selector.shelf"));
+        const space_title = cx_ui.p("space-title", lang.get("selector.space"));
+
+        shelf.appendChild(shelf_icon);
+        shelf.appendChild(shelf_title);
+        
+        space.appendChild(space_icon);
+        space.appendChild(space_title);
+
+        container.appendChild(shelf);
+        container.appendChild(space);
+
+        return container;
+    }
+}

+ 21 - 0
source/scripts/assets/shelf.js

@@ -0,0 +1,21 @@
+import { cx_ui } from "./cx-ui";
+import { dictionary } from "./dictionary";
+import { selector } from "./selector";
+import { view } from "./view";
+
+export class shelf extends view {
+    #manager;
+
+    constructor(manager) {
+        super();
+        this.#manager = manager;
+    }
+
+    show() {
+        return cx_ui.push("App", () => {
+            this.#manager.view = new selector(this.#manager);
+        }, (button) => {
+            button.value = new dictionary().get("selector.return");
+        });
+    }
+}

+ 21 - 0
source/scripts/assets/space.js

@@ -0,0 +1,21 @@
+import { view } from "./view";
+import { cx_ui } from "./cx-ui.js";
+import { selector } from "./selector.js";
+import { dictionary } from "./dictionary.js";
+
+export class space extends view {
+    #manager;
+
+    constructor(manager) {
+        super();
+        this.#manager = manager;
+    }
+
+    show() {
+        return cx_ui.push("return", () => {
+            this.#manager.view = new selector(this.#manager); 
+        }, (button) => {
+            button.value = new dictionary().get("selector.return");    
+        });
+    }
+}

+ 9 - 0
source/scripts/assets/view.js

@@ -0,0 +1,9 @@
+export class view {
+    show() {
+        throw new Error("This function must be override.");
+    }
+
+    destroy() {
+
+    }
+}

+ 7 - 0
source/scripts/languages/pl_pl.js

@@ -0,0 +1,7 @@
+export const language_pl_pl = {
+    "selector": {
+        "shelf": "Sprzęty",
+        "space": "Spacer",
+        "return": "Powrót"
+    }
+};

+ 17 - 1
source/scripts/loader.js

@@ -1,3 +1,19 @@
 import { cx_ui } from "./assets/cx-ui.js";
+import { screen } from "./assets/screen.js";
+import { selector } from "./assets/selector.js";
+import { loading_screen } from "./assets/loading-screen.js";
 
-console.log(cx_ui);
+document.addEventListener("DOMContentLoaded", () => {
+    if (!document.querySelector("body")) {
+        document.querySelector("html").appendChild(
+            document.createElement("body")
+        );
+    }
+
+    const app = document.querySelector(".app");
+    const view_manager = new screen(app);
+
+    setTimeout(() => {
+        view_manager.view = new selector(view_manager);
+    }, 500);
+});

+ 84 - 0
source/static/icons/shelf.svg

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="140.15248mm"
+   height="66.821518mm"
+   viewBox="0 0 140.15248 66.821518"
+   version="1.1"
+   id="svg1"
+   inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
+   sodipodi:docname="shelf.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview1"
+     pagecolor="#ffffff"
+     bordercolor="#000000"
+     borderopacity="0.25"
+     inkscape:showpageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1"
+     inkscape:document-units="mm"
+     inkscape:zoom="1.4930696"
+     inkscape:cx="303.4018"
+     inkscape:cy="37.171744"
+     inkscape:window-width="1840"
+     inkscape:window-height="977"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="g6" />
+  <defs
+     id="defs1" />
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-26.784309,-60.268365)">
+    <g
+       id="g7">
+      <rect
+         style="fill:#6c6753;stroke:#000000;stroke-width:4.065;stroke-dasharray:none;stroke-opacity:1"
+         id="rect1"
+         width="136.08748"
+         height="15.043117"
+         x="28.816809"
+         y="110.01427"
+         ry="7.5215583"
+         rx="7.51829" />
+      <g
+         id="g6"
+         transform="translate(-2.3594971,-2.0048756)">
+        <rect
+           style="fill:#71c837;stroke:#000000;stroke-width:4.065;stroke-dasharray:none;stroke-opacity:1"
+           id="rect6"
+           width="31.576786"
+           height="30.824961"
+           x="48.367622"
+           y="74.368767"
+           ry="5.3880939"
+           rx="4.5109701" />
+        <rect
+           style="fill:#ff9955;stroke:#000000;stroke-width:4.065;stroke-dasharray:none;stroke-opacity:1"
+           id="rect6-5"
+           width="30.111774"
+           height="40.887985"
+           x="119.96069"
+           y="64.30574"
+           ry="4.3237448"
+           rx="4.3016825" />
+        <ellipse
+           style="fill:#8800aa;stroke:#000000;stroke-width:4.065;stroke-dasharray:none;stroke-opacity:1"
+           id="path6"
+           cx="99.952553"
+           cy="91.622421"
+           rx="12.278799"
+           ry="13.571304" />
+      </g>
+    </g>
+  </g>
+</svg>

+ 98 - 0
source/static/icons/space.svg

@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="106.66824mm"
+   height="53.151524mm"
+   viewBox="0 0 106.66824 53.151524"
+   version="1.1"
+   id="svg1"
+   inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
+   sodipodi:docname="space.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview1"
+     pagecolor="#ffffff"
+     bordercolor="#000000"
+     borderopacity="0.25"
+     inkscape:showpageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1"
+     inkscape:document-units="mm"
+     inkscape:zoom="1.4930696"
+     inkscape:cx="214.65845"
+     inkscape:cy="101.13393"
+     inkscape:window-width="1920"
+     inkscape:window-height="1080"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="layer1" />
+  <defs
+     id="defs1">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="253.11552 : -80.061301 : 1"
+       inkscape:vp_y="0 : 999.99997 : 0"
+       inkscape:vp_z="-308.26031 : -83.820451 : 1"
+       inkscape:persp3d-origin="105 : -144.84848 : 1"
+       id="perspective8" />
+  </defs>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-52.724651,-130.16439)">
+    <g
+       id="g8">
+      <rect
+         style="fill:#8800aa;stroke:#000000;stroke-width:4.06501;stroke-linecap:round;stroke-linejoin:round"
+         id="rect5"
+         width="102.60323"
+         height="49.086514"
+         x="54.757156"
+         y="132.1969"
+         rx="4.3016825"
+         ry="4.3237448" />
+      <g
+         id="g7"
+         transform="translate(2.1264916,-0.44301319)">
+        <circle
+           style="fill:#37c8ab;stroke:#000000;stroke-width:4.06501;stroke-linecap:round;stroke-linejoin:round"
+           id="path5"
+           cx="136.8929"
+           cy="149.29742"
+           r="10.366647" />
+        <g
+           id="g6">
+          <rect
+             style="fill:#aad400;stroke:#000000;stroke-width:4.06501;stroke-linecap:round;stroke-linejoin:round"
+             id="rect6"
+             width="15.483311"
+             height="9.5692129"
+             x="129.15125"
+             y="165.86635"
+             rx="4.3016825"
+             ry="4.3237448" />
+          <rect
+             style="fill:#aad400;stroke:#000000;stroke-width:4.06501;stroke-linecap:round;stroke-linejoin:round"
+             id="rect6-2"
+             width="15.483311"
+             height="9.5692129"
+             x="99.911133"
+             y="165.86635"
+             rx="4.3016825"
+             ry="4.3237448" />
+        </g>
+      </g>
+    </g>
+    <path
+       id="rect8"
+       style="fill:#00aa00;stroke:#000000;stroke-width:4.06501;stroke-linecap:round;stroke-linejoin:round"
+       d="m 77.369624,144.33549 c -2.383132,0 -4.301546,1.92841 -4.301546,4.32377 v 2.76469 h -2.764689 c -2.395355,0 -4.323767,1.91841 -4.323767,4.30154 v 2.02934 c 0,2.38313 1.928412,4.30154 4.323767,4.30154 h 2.764689 v 2.76469 c 0,2.39536 1.918414,4.32377 4.301546,4.32377 h 2.029334 c 2.383132,0 4.301546,-1.92841 4.301546,-4.32377 v -2.76469 h 2.764689 c 2.395355,0 4.323772,-1.91841 4.323772,-4.30154 v -2.02934 c 0,-2.38313 -1.928417,-4.30154 -4.323772,-4.30154 h -2.764689 v -2.76469 c 0,-2.39536 -1.918414,-4.32377 -4.301546,-4.32377 z" />
+  </g>
+</svg>

+ 8 - 3
source/templates/view.html

@@ -4,9 +4,14 @@
     <head>
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+        <title>{{ app_name }}</title>
         
-        <title><!-- app_name --></title>
-        
-        <!-- bundle_items -->
+        {{ bundle_items }}
     </head>
+
+    <body>
+        <div class="loading-screen"></div>
+        <div class="app"></div>
+    </body>
 </html>

+ 3 - 0
source/theme/colors.sass

@@ -0,0 +1,3 @@
+$background-color: #011627
+$primary-color: #5B8E7D
+$content-color: #FFFFFF

+ 9 - 0
source/theme/fonts.sass

@@ -0,0 +1,9 @@
+@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap')
+
+body 
+    font-family: "Noto Sans", serif
+    font-optical-sizing: auto
+    font-weight: 400
+    font-style: normal
+    font-variation-settings: "wdth" 100
+    font-size: 14px

+ 5 - 1
source/theme/loader.sass

@@ -1 +1,5 @@
-@import "document"
+@import "colors"
+@import "settings"
+@import "fonts"
+@import "document"
+@import "selector"

+ 14 - 0
source/theme/loading-screen.sass

@@ -0,0 +1,14 @@
+@import "colors"
+
+.loading-screen
+    position: fixed
+    top: 0px
+    left: 0px
+    width: 100%
+    height: 100%
+    z-index: 100000
+    background-color: $primary-color
+    transition: width 1s
+
+.loading-screen.hidden
+    width: 0px !important     

+ 39 - 0
source/theme/selector.sass

@@ -0,0 +1,39 @@
+.selector
+    width: 100vw
+    min-height: 100vh
+    margin: 0px
+    padding: 0px
+    display: flex
+    flex-direction: row
+    align-items: center
+    justify-content: center
+    gap: 50px
+    background-color: $background-color
+
+    div
+        max-width: 300px
+        border-radius: $padding
+        padding: $padding
+        box-sizing: border-box
+        border: $border
+        color: $primary-color
+        transition: border-color 0.5s, transform 0.5s, color 0.5s
+        display: flex
+        flex-direction: column
+        align-items: center
+        justify-content: center
+
+        p
+            font-weight: 700
+            font-size: 1.5em
+
+        img
+            width: 100%
+
+    @media (max-width: 800px)
+        flex-direction: column
+
+    div:hover 
+        transform: scale(1.1)
+        border-color: $content-color
+        color: $content-color

+ 2 - 0
source/theme/settings.sass

@@ -0,0 +1,2 @@
+$padding: 20px
+$border: $primary-color 3px solid