spaday-regular-layout 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ from pathlib import Path
2
+
3
+ from spaday import ComponentPackage
4
+
5
+ from .components import RegularLayoutFrame, SpadayRegularLayout
6
+
7
+ __version__ = "0.1.0"
8
+
9
+ package = ComponentPackage(
10
+ name="regular-layout",
11
+ assets_dir=Path(__file__).parent / "extension",
12
+ assets=(("css", "css/lorax.css"), ("js", "cdn/index.js")),
13
+ )
14
+
15
+ RegularLayout = SpadayRegularLayout
16
+
17
+ __all__ = ["RegularLayout", "RegularLayoutFrame", "package"]
@@ -0,0 +1,40 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "modules": [
4
+ {
5
+ "path": "src/ts/index.ts",
6
+ "declarations": [
7
+ {
8
+ "kind": "class",
9
+ "name": "RegularLayout",
10
+ "customElement": true,
11
+ "tagName": "spaday-regular-layout",
12
+ "summary": "Resizable panel layout driven by a serializable split/tab tree.",
13
+ "attributes": [
14
+ {
15
+ "name": "layout",
16
+ "type": { "text": "unknown" },
17
+ "description": "Serializable regular-layout split/tab tree."
18
+ }
19
+ ],
20
+ "slots": [{ "name": "" }]
21
+ },
22
+ {
23
+ "kind": "class",
24
+ "name": "RegularLayoutFrame",
25
+ "customElement": true,
26
+ "tagName": "regular-layout-frame",
27
+ "summary": "Named draggable panel frame inside a RegularLayout.",
28
+ "attributes": [
29
+ {
30
+ "name": "name",
31
+ "type": { "text": "string" },
32
+ "description": "Panel name referenced by the layout tree."
33
+ }
34
+ ],
35
+ "slots": [{ "name": "" }]
36
+ }
37
+ ]
38
+ }
39
+ ]
40
+ }
@@ -0,0 +1,52 @@
1
+ # Generated by spaday.cem from components.cem.json — do not edit by hand.
2
+ from __future__ import annotations
3
+
4
+ from typing import Any
5
+
6
+ from spaday.component import Child, Component
7
+
8
+ __all__ = ["RegularLayoutFrame", "SpadayRegularLayout"]
9
+
10
+
11
+ class SpadayRegularLayout(Component):
12
+ """Resizable panel layout driven by a serializable split/tab tree."""
13
+
14
+ tag = "spaday-regular-layout"
15
+
16
+ def __init__(
17
+ self,
18
+ *children: Child,
19
+ key: str | None = None,
20
+ layout: Any = None,
21
+ **props: Any,
22
+ ) -> None:
23
+ super().__init__(
24
+ *children,
25
+ key=key,
26
+ props={
27
+ "layout": layout,
28
+ },
29
+ **props,
30
+ )
31
+
32
+
33
+ class RegularLayoutFrame(Component):
34
+ """Named draggable panel frame inside a RegularLayout."""
35
+
36
+ tag = "regular-layout-frame"
37
+
38
+ def __init__(
39
+ self,
40
+ *children: Child,
41
+ key: str | None = None,
42
+ name: str | None = None,
43
+ **props: Any,
44
+ ) -> None:
45
+ super().__init__(
46
+ *children,
47
+ key=key,
48
+ props={
49
+ "name": name,
50
+ },
51
+ **props,
52
+ )
@@ -0,0 +1,113 @@
1
+ import asyncio
2
+ import logging
3
+ from copy import deepcopy
4
+ from typing import Any
5
+
6
+ import transports
7
+ import uvicorn
8
+ from pydantic import BaseModel
9
+ from spaday import CallEndpoint, If, Wire, element, event_value, not_, prop, this
10
+ from spaday.backends.starlette import serve
11
+ from starlette.responses import JSONResponse
12
+ from starlette.routing import Route, WebSocketRoute
13
+
14
+ from spaday_regular_layout import RegularLayout, RegularLayoutFrame, package
15
+
16
+ logger = logging.getLogger("uvicorn.error")
17
+
18
+ initial_layout = {
19
+ "type": "split-layout",
20
+ "orientation": "horizontal",
21
+ "sizes": [0.28, 0.72],
22
+ "children": [
23
+ {"type": "tab-layout", "tabs": ["navigation"], "selected": 0},
24
+ {
25
+ "type": "split-layout",
26
+ "orientation": "vertical",
27
+ "sizes": [0.68, 0.32],
28
+ "children": [
29
+ {"type": "tab-layout", "tabs": ["workspace"], "selected": 0},
30
+ {"type": "tab-layout", "tabs": ["activity"], "selected": 0},
31
+ ],
32
+ },
33
+ ],
34
+ }
35
+
36
+ alternate_layout = {
37
+ "type": "split-layout",
38
+ "orientation": "vertical",
39
+ "sizes": [0.65, 0.35],
40
+ "children": [
41
+ {"type": "tab-layout", "tabs": ["workspace", "navigation"], "selected": 0},
42
+ {"type": "tab-layout", "tabs": ["activity"], "selected": 0},
43
+ ],
44
+ }
45
+
46
+
47
+ class LayoutFeed(BaseModel):
48
+ layout: dict[str, Any]
49
+
50
+
51
+ layout_feed = LayoutFeed(layout=initial_layout)
52
+ session = transports.Session()
53
+ session.host(layout_feed)
54
+ server = transports.Server(session)
55
+
56
+
57
+ async def rotate_layout() -> None:
58
+ layouts = (alternate_layout, initial_layout)
59
+ index = 0
60
+ while True:
61
+ await asyncio.sleep(5)
62
+ layout_feed.layout = deepcopy(layouts[index])
63
+ logger.info("Server pushed layout %s", index + 1)
64
+ index = (index + 1) % len(layouts)
65
+
66
+
67
+ async def save_layout(request):
68
+ updated = await request.json()
69
+ if updated != layout_feed.layout:
70
+ layout_feed.layout = updated
71
+ logger.info("Layout received from browser: %s", updated)
72
+ return JSONResponse({"saved": True})
73
+
74
+
75
+ def panel(title: str, copy: str, name: str) -> RegularLayoutFrame:
76
+ return RegularLayoutFrame(
77
+ element("h2").text(title),
78
+ element("p").text(copy),
79
+ name=name,
80
+ style="padding: 1rem; box-sizing: border-box; font-family: system-ui",
81
+ )
82
+
83
+
84
+ page = (
85
+ RegularLayout(
86
+ panel("Navigation", "Drag this panel's tab to rearrange it.", "navigation"),
87
+ panel("Workspace", "Drop another tab here to create a stack.", "workspace"),
88
+ panel("Activity", "Drag either divider to resize nested splits.", "activity"),
89
+ layout=initial_layout,
90
+ style="height: calc(100vh - 2rem); margin: 1rem",
91
+ )
92
+ .prop("class", "lorax")
93
+ .bind("layout", "layout")
94
+ .on(
95
+ "regular-layout-update",
96
+ If(not_(prop(this(), "restoring")), CallEndpoint("POST", "/api/layout", event_value())),
97
+ )
98
+ )
99
+
100
+ app = serve(
101
+ page,
102
+ packages=[package],
103
+ wire=[Wire("/ws", flatten=False)],
104
+ routes=[
105
+ WebSocketRoute("/ws", transports.ws_endpoint(server)),
106
+ Route("/api/layout", save_layout, methods=["POST"]),
107
+ ],
108
+ background=[transports.autosync(server), rotate_layout()],
109
+ title="spaday-regular-layout example",
110
+ )
111
+
112
+ if __name__ == "__main__":
113
+ uvicorn.run(app, host="127.0.0.1", port=8013)