HTeaLeaf 0.3.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.
- HTeaLeaf/Html/Component.py +219 -0
- HTeaLeaf/Html/Elements.py +137 -0
- HTeaLeaf/Html/__init__.py +2 -0
- HTeaLeaf/Magic/HelperMidleware.py +13 -0
- HTeaLeaf/Magic/LocalState.py +38 -0
- HTeaLeaf/Magic/MagicComponent.py +122 -0
- HTeaLeaf/Magic/Store.py +180 -0
- HTeaLeaf/Magic/__init__.py +0 -0
- HTeaLeaf/Magic/jslib/JSCode.py +110 -0
- HTeaLeaf/Magic/jslib/JSDO.py +63 -0
- HTeaLeaf/Magic/jslib/__init__.py +2 -0
- HTeaLeaf/Magic/jslib/common.py +43 -0
- HTeaLeaf/Magic/jslib/py2js.py +307 -0
- HTeaLeaf/Server/ASGI.py +80 -0
- HTeaLeaf/Server/CGI.py +46 -0
- HTeaLeaf/Server/Http/HttpHeader.py +40 -0
- HTeaLeaf/Server/Http/HttpRequest.py +81 -0
- HTeaLeaf/Server/Http/HttpResponse.py +75 -0
- HTeaLeaf/Server/Http/__init__.py +0 -0
- HTeaLeaf/Server/Server.py +236 -0
- HTeaLeaf/Server/WSGI.py +37 -0
- HTeaLeaf/Server/__init__.py +0 -0
- HTeaLeaf/__init__.py +0 -0
- HTeaLeaf/utils.py +3 -0
- htealeaf-0.3.0.dist-info/METADATA +95 -0
- htealeaf-0.3.0.dist-info/RECORD +28 -0
- htealeaf-0.3.0.dist-info/WHEEL +5 -0
- htealeaf-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import json
|
|
3
|
+
from typing import Any, List, Union
|
|
4
|
+
|
|
5
|
+
from ..Magic.jslib.JSCode import JSCode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Component:
|
|
9
|
+
"""
|
|
10
|
+
Represents an HTML component with attributes, children, and optional inline styles.
|
|
11
|
+
This class allows constructing HTML elements programmatically and managing CSS styles.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, name, *childs: Union[str, List[Any], "Component", "JSCode"]) -> None:
|
|
15
|
+
"""
|
|
16
|
+
Initializes a new Component instance.
|
|
17
|
+
|
|
18
|
+
:param name: The tag name of the HTML element.
|
|
19
|
+
:param childs: Optional children elements, which can be strings, lists, or other Component instances.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
self.styles: str | None = None
|
|
23
|
+
self.name = name
|
|
24
|
+
self.children: list[Component | str | list | JSCode] = list(childs)
|
|
25
|
+
self.attributes: dict[str, str | None] = dict()
|
|
26
|
+
self._id: str = self._generate_id()
|
|
27
|
+
|
|
28
|
+
def _generate_id(self) -> str:
|
|
29
|
+
"""Genera un ID determinista basado en el contenido del componente."""
|
|
30
|
+
content = {
|
|
31
|
+
"name": self.name,
|
|
32
|
+
"children": [
|
|
33
|
+
child._id if isinstance(child, Component) else str(child)
|
|
34
|
+
for child in self.children
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
raw = json.dumps(content, sort_keys=True)
|
|
38
|
+
hash_str = hashlib.md5(raw.encode()).hexdigest()[:12]
|
|
39
|
+
return f"tl-{hash_str}"
|
|
40
|
+
|
|
41
|
+
def id(self, id: str):
|
|
42
|
+
"""
|
|
43
|
+
Sets the ID of the component and adds it as an attribute.
|
|
44
|
+
|
|
45
|
+
:param id: The ID to assign.
|
|
46
|
+
:return: The component instance (for method chaining).
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
self._id = id
|
|
50
|
+
return self.attr(id=id)
|
|
51
|
+
|
|
52
|
+
def classes(self, classes):
|
|
53
|
+
"""
|
|
54
|
+
Adds a CSS class attribute to the component.
|
|
55
|
+
|
|
56
|
+
:param classes: CSS class names (space-separated).
|
|
57
|
+
:return: The component instance (for method chaining).
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
self.attributes["class"] = classes
|
|
61
|
+
return self
|
|
62
|
+
|
|
63
|
+
def style(self, path: str | None = None, **attr):
|
|
64
|
+
"""
|
|
65
|
+
Adds inline styles to the component.
|
|
66
|
+
|
|
67
|
+
:param path: Optional path to an external CSS file.
|
|
68
|
+
:param attr: CSS properties to apply (e.g., color="red", margin="10px").
|
|
69
|
+
:return: The component instance (for method chaining).
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
self.styles = (self.styles or "") + f"#{self._id} {{\n"
|
|
74
|
+
self.styles += "\n".join(
|
|
75
|
+
f" {k.replace('_', '-')}: {v};" for k, v in attr.items()
|
|
76
|
+
)
|
|
77
|
+
self.styles += "\n}\n"
|
|
78
|
+
|
|
79
|
+
if path:
|
|
80
|
+
with open(path, "r") as f:
|
|
81
|
+
self.styles += f.read()
|
|
82
|
+
return self
|
|
83
|
+
|
|
84
|
+
def attr(self,*args, **attr):
|
|
85
|
+
"""
|
|
86
|
+
Adds custom attributes to the component.
|
|
87
|
+
|
|
88
|
+
:param attr: Dictionary of attribute names and values.
|
|
89
|
+
:return: The component instance (for method chaining).
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
for arg in args:
|
|
93
|
+
self.attributes[arg] = None
|
|
94
|
+
|
|
95
|
+
for k in attr:
|
|
96
|
+
# if type(attr[k]) is str:
|
|
97
|
+
value = attr[k]
|
|
98
|
+
# if isinstance(value, JSCode):
|
|
99
|
+
# value = f"{{{{{str(value)}}}}}"
|
|
100
|
+
self.attributes[k] = value
|
|
101
|
+
# elif type(attr[k]) is FunctionType:
|
|
102
|
+
# py_f = inspect.getsource(attr[k])
|
|
103
|
+
# self.attributes[k] = f"""() => pyodide.runPython(`{py_f}`)"""
|
|
104
|
+
|
|
105
|
+
return self
|
|
106
|
+
|
|
107
|
+
def append(self, child: Union[str, "Component", list]):
|
|
108
|
+
"""
|
|
109
|
+
Appends a child element to the component.
|
|
110
|
+
|
|
111
|
+
:param child: A Component, string, or list of elements.
|
|
112
|
+
:return: The component instance (for method chaining).
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
self.children.append(child)
|
|
116
|
+
return self
|
|
117
|
+
|
|
118
|
+
def prepend(self, child: Union[str, "Component", list]):
|
|
119
|
+
"""
|
|
120
|
+
Prepends a child element to the component.
|
|
121
|
+
|
|
122
|
+
:param child: A Component, string, or list of elements.
|
|
123
|
+
:return: The component instance (for method chaining).
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
self.children.insert(0, child)
|
|
127
|
+
return self
|
|
128
|
+
|
|
129
|
+
def __build_attr__(self) -> str:
|
|
130
|
+
return " " + " ".join(
|
|
131
|
+
f"{k}='{v}'" if v is not None else f"{k}" for k, v in self.attributes.items()
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def __build_child__(self, children: list):
|
|
135
|
+
html_parts = []
|
|
136
|
+
css_parts = []
|
|
137
|
+
for child in children:
|
|
138
|
+
if isinstance(child, str):
|
|
139
|
+
html_parts.append(f"{child}")
|
|
140
|
+
elif isinstance(child, list):
|
|
141
|
+
html, css = self.__build_child__(child)
|
|
142
|
+
html_parts.append(html)
|
|
143
|
+
css_parts.append(css)
|
|
144
|
+
elif isinstance(child, Component):
|
|
145
|
+
html, css = child.build()
|
|
146
|
+
html_parts.append(html)
|
|
147
|
+
css_parts.append(css)
|
|
148
|
+
# elif isinstance(child, JSDO):
|
|
149
|
+
# print(f"JSCode: {child().raw}")
|
|
150
|
+
# html_parts.append(f"{{{{{child().raw}}}}}")
|
|
151
|
+
elif isinstance(child, JSCode):
|
|
152
|
+
# JSCode outside of an attribute should be a special tag {{jscode_name}}
|
|
153
|
+
print(f"JSCode: {child.raw}")
|
|
154
|
+
html_parts.append(f"{{{{{child.raw}}}}}")
|
|
155
|
+
else:
|
|
156
|
+
try:
|
|
157
|
+
html_parts.append(str(child))
|
|
158
|
+
except Exception:
|
|
159
|
+
continue
|
|
160
|
+
return "".join(html_parts), "".join(filter(None, css_parts))
|
|
161
|
+
|
|
162
|
+
def build(self) -> tuple[str, str]:
|
|
163
|
+
"""
|
|
164
|
+
Builds the component's HTML and CSS separately.
|
|
165
|
+
|
|
166
|
+
:return: A tuple (HTML string, CSS string)
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
if self.styles is not None and "id" not in self.attributes:
|
|
170
|
+
self.attr(id=self._id)
|
|
171
|
+
if len(self.children) == 0:
|
|
172
|
+
result = f"<{self.name}{self.__build_attr__()}/>\n"
|
|
173
|
+
else:
|
|
174
|
+
endln = "\n" if len(self.children) > 1 else ""
|
|
175
|
+
result = f"<{self.name}{self.__build_attr__()}>{endln}"
|
|
176
|
+
html, styles = self.__build_child__(self.children)
|
|
177
|
+
result += html
|
|
178
|
+
if self.styles is None:
|
|
179
|
+
self.styles = styles
|
|
180
|
+
else:
|
|
181
|
+
self.styles += styles
|
|
182
|
+
result += f"\t</{self.name}>\n"
|
|
183
|
+
css: str = "" if self.styles is None else self.styles
|
|
184
|
+
return result, css
|
|
185
|
+
|
|
186
|
+
def render(self) -> str:
|
|
187
|
+
"""
|
|
188
|
+
Builds and returns the full HTML including inline CSS inside a <style> tag.
|
|
189
|
+
|
|
190
|
+
:return: A complete HTML string with embedded CSS.
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
if len(self.children) == 0:
|
|
194
|
+
result = f"<{self.name}{self.__build_attr__()}/>\n"
|
|
195
|
+
else:
|
|
196
|
+
inner_result, css = self.__build_child__(self.children)
|
|
197
|
+
if self.styles is None:
|
|
198
|
+
self.styles = css
|
|
199
|
+
else:
|
|
200
|
+
self.styles += css
|
|
201
|
+
self.styles += "\n"
|
|
202
|
+
result = f"<{self.name}{self.__build_attr__()}>\n"
|
|
203
|
+
if self.styles is not None:
|
|
204
|
+
result += f"<style>{self.styles}</style>\n"
|
|
205
|
+
result += inner_result
|
|
206
|
+
result += f"</{self.name}>\n"
|
|
207
|
+
|
|
208
|
+
return result
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class ComponentMeta(type):
|
|
212
|
+
def __new__(cls, name, bases, dct):
|
|
213
|
+
if name not in ("Component", "ComponentMeta"):
|
|
214
|
+
|
|
215
|
+
def init(self, *childs):
|
|
216
|
+
super(self.__class__, self).__init__(name, *childs)
|
|
217
|
+
|
|
218
|
+
dct["__init__"] = init
|
|
219
|
+
return super().__new__(cls, name, bases, dct)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from types import FunctionType
|
|
2
|
+
from typing import Any, List, Union
|
|
3
|
+
|
|
4
|
+
from HTeaLeaf.Magic.jslib import JSCode, JSFunction, js
|
|
5
|
+
|
|
6
|
+
from .Component import Component, ComponentMeta
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class html(Component, metaclass=ComponentMeta):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
class head(Component, metaclass=ComponentMeta):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
class header(Component, metaclass=ComponentMeta):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
class link(Component, metaclass=ComponentMeta):
|
|
19
|
+
def __init__(self, *childs):
|
|
20
|
+
super().__init__("link", *childs)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class script(Component):
|
|
24
|
+
def __init__(self, *childs: Union[str, List[Any], "Component", JSFunction] ,src=None):
|
|
25
|
+
parsed_childs: Union[str, List[Any], "Component"] = []
|
|
26
|
+
for child in childs:
|
|
27
|
+
if type(child) is FunctionType:
|
|
28
|
+
parsed_childs.append(js(child))
|
|
29
|
+
elif type(child) is JSFunction:
|
|
30
|
+
parsed_childs.append(child)
|
|
31
|
+
else:
|
|
32
|
+
parsed_childs.append(child)
|
|
33
|
+
super().__init__("script", *parsed_childs)
|
|
34
|
+
self.unsafe = True
|
|
35
|
+
if src is not None:
|
|
36
|
+
self.attr(src=src)
|
|
37
|
+
self.children = [""]
|
|
38
|
+
# else:
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class style(Component, metaclass=ComponentMeta):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class body(Component, metaclass=ComponentMeta):
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class h1(Component, metaclass=ComponentMeta):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class h2(Component, metaclass=ComponentMeta):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class h3(Component, metaclass=ComponentMeta):
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class div(Component, metaclass=ComponentMeta):
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
def row(self):
|
|
66
|
+
self.attr(style="display: flex; flex-direction: row")
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
def column(self):
|
|
70
|
+
self.attr(style="display: flex; flex-direction: column")
|
|
71
|
+
return self
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class button(Component, metaclass=ComponentMeta):
|
|
75
|
+
|
|
76
|
+
def reactive(self,path,component_id):
|
|
77
|
+
"""
|
|
78
|
+
Makes the button reactive by linking it to a FetchComponent.
|
|
79
|
+
|
|
80
|
+
:param path: The URL to fetch new data from when clicked.
|
|
81
|
+
:param component: The FetchComponent to be updated.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
js = f"""fetchAndUpdate('{path}','{{}}','{component_id}')"""
|
|
85
|
+
self.attr(onclick=js)
|
|
86
|
+
return self
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class label(Component, metaclass=ComponentMeta):
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
class checkbox(Component):
|
|
94
|
+
def __init__(self,checked = False, *childs):
|
|
95
|
+
super().__init__("input", *childs)
|
|
96
|
+
self.attr(type="checkbox")
|
|
97
|
+
if checked:
|
|
98
|
+
self.attr(checked="True")
|
|
99
|
+
|
|
100
|
+
class textInput(Component):
|
|
101
|
+
def __init__(self, *childs):
|
|
102
|
+
super().__init__("input", *childs)
|
|
103
|
+
|
|
104
|
+
class select(Component):
|
|
105
|
+
def __init__(self, items: List[str]):
|
|
106
|
+
super().__init__("select")
|
|
107
|
+
for item in items:
|
|
108
|
+
self.append(option(item))
|
|
109
|
+
|
|
110
|
+
class option(Component):
|
|
111
|
+
def __init__(self, value):
|
|
112
|
+
super().__init__("option", value)
|
|
113
|
+
self.attr(value=value)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class submit(Component):
|
|
117
|
+
def __init__(self, *childs):
|
|
118
|
+
super().__init__("input", *childs)
|
|
119
|
+
self.attr(type="submit")
|
|
120
|
+
|
|
121
|
+
class form(Component):
|
|
122
|
+
def __init__(self, *childs):
|
|
123
|
+
super().__init__("form", *childs)
|
|
124
|
+
|
|
125
|
+
def action(self, action):
|
|
126
|
+
self.attr(action=action)
|
|
127
|
+
return self
|
|
128
|
+
|
|
129
|
+
def method(self, method):
|
|
130
|
+
self.attr(method=method)
|
|
131
|
+
return self
|
|
132
|
+
|
|
133
|
+
def tl_if(condition: JSCode | str | bool, *childs):
|
|
134
|
+
if isinstance(condition, str):
|
|
135
|
+
condition = JSCode(condition)
|
|
136
|
+
|
|
137
|
+
return div(*childs).attr(style=f"display: {condition};")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from ..Html.Component import Component
|
|
2
|
+
from ..Html.Elements import script
|
|
3
|
+
from ..Server.Server import Server, ServerEvent
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def enable_reactivity(server: Server):
|
|
7
|
+
helper_script = script(src="_engine/helper.js")
|
|
8
|
+
|
|
9
|
+
def event_handler(res_code, res_body, res_headers):
|
|
10
|
+
if isinstance(res_body, Component):
|
|
11
|
+
res_body.prepend(helper_script)
|
|
12
|
+
|
|
13
|
+
server.registry_hook(ServerEvent.on_response, event_handler)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import inspect
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from HTeaLeaf.Html.Elements import script
|
|
6
|
+
|
|
7
|
+
from ..Magic.jslib.JSCode import JSCode
|
|
8
|
+
from ..Magic.jslib.JSDO import JSDO
|
|
9
|
+
|
|
10
|
+
# class localState():
|
|
11
|
+
# def __init__(self, init_state):
|
|
12
|
+
# self.do = JSDO("LocalState",init_state)
|
|
13
|
+
|
|
14
|
+
# def
|
|
15
|
+
|
|
16
|
+
# def js(self):
|
|
17
|
+
# return self.do.js()
|
|
18
|
+
|
|
19
|
+
# def get(self):
|
|
20
|
+
# return self.do.get()
|
|
21
|
+
|
|
22
|
+
# def set(self, data):
|
|
23
|
+
# return self.do.set(data)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def use_state(init_state):
|
|
28
|
+
frame = inspect.stack()[1]
|
|
29
|
+
site = f"{frame.filename}:{frame.lineno}"
|
|
30
|
+
raw = f"{site}:{json.dumps(init_state, sort_keys=True)}"
|
|
31
|
+
id = hashlib.md5(raw.encode()).hexdigest()[:12]
|
|
32
|
+
name = f"localstate_{id}"
|
|
33
|
+
|
|
34
|
+
def new():
|
|
35
|
+
return script(f"const {name} = new LocalState({json.dumps(init_state, sort_keys=True)},\"{name}\");")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
return new, JSCode(name)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from ..Html.Component import Component
|
|
7
|
+
from ..Html.Elements import div, script
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FetchComponent(Component):
|
|
11
|
+
"""
|
|
12
|
+
A component that fetches data from a given URL and updates its content dynamically.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, url, body: str | dict | None = None) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Initializes a FetchComponent that loads content asynchronously.
|
|
18
|
+
|
|
19
|
+
:param url: The URL to fetch data from.
|
|
20
|
+
:param body: Optional request body for POST requests.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
self._reid = new_id()
|
|
24
|
+
placeholder = div("Loading...").id(self._reid)
|
|
25
|
+
super().__init__("div", placeholder)
|
|
26
|
+
# Configuración de la petición
|
|
27
|
+
config = {"method": "POST" if body is not None else "GET", "body": body }
|
|
28
|
+
# Serializar la configuración en JSON para JS
|
|
29
|
+
_js_file = os.path.dirname(__file__) + "/MagicComponent.js"
|
|
30
|
+
url = json.dumps(url)
|
|
31
|
+
config_js = json.dumps(config)
|
|
32
|
+
_id = json.dumps(placeholder._id)
|
|
33
|
+
js: str = f"fetchAndUpdate({url},{config_js},{placeholder._id})"
|
|
34
|
+
|
|
35
|
+
self.append(script(js))
|
|
36
|
+
|
|
37
|
+
def reid(self):
|
|
38
|
+
"""
|
|
39
|
+
Returns the reactive ID of the component.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
return self._reid
|
|
43
|
+
|
|
44
|
+
class rButton(Component):
|
|
45
|
+
"""
|
|
46
|
+
A reactive button that triggers updates on FetchComponents.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, *childs):
|
|
50
|
+
"""
|
|
51
|
+
Initializes an rButton.
|
|
52
|
+
|
|
53
|
+
:param childs: The child elements (text or components) inside the button.
|
|
54
|
+
"""
|
|
55
|
+
super().__init__("button", *childs)
|
|
56
|
+
|
|
57
|
+
def reactive(self,path,component: FetchComponent):
|
|
58
|
+
"""
|
|
59
|
+
Makes the button reactive by linking it to a FetchComponent.
|
|
60
|
+
|
|
61
|
+
:param path: The URL to fetch new data from when clicked.
|
|
62
|
+
:param component: The FetchComponent to be updated.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
#config = {"method": "GET"}
|
|
66
|
+
if not hasattr(component, "reid"):
|
|
67
|
+
raise Exception("component is not reactive")
|
|
68
|
+
id = component.reid()
|
|
69
|
+
# Serializar la configuración en JSON para JS
|
|
70
|
+
#config_js = json.dumps(config)
|
|
71
|
+
js = f"""fetchAndUpdate('{path}','{{}}','{id}')"""
|
|
72
|
+
self.attr(onclick=js)
|
|
73
|
+
return self
|
|
74
|
+
|
|
75
|
+
#def refresh(self, path)
|
|
76
|
+
|
|
77
|
+
class HydratedComponent(Component):
|
|
78
|
+
"""
|
|
79
|
+
WIP🚧
|
|
80
|
+
A component that supports server-side hydration for dynamic updates.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(self, *childs):
|
|
84
|
+
"""
|
|
85
|
+
Initializes a HydratedComponent.
|
|
86
|
+
|
|
87
|
+
:param childs: The child elements inside the component.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class PoolComponent(Component):
|
|
94
|
+
"""
|
|
95
|
+
WIP🚧
|
|
96
|
+
A component that periodically fetches data from a URL and updates itself.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def __init__(self, child: Component, url: str, interval: int = 5000):
|
|
100
|
+
"""
|
|
101
|
+
Initializes a PoolComponent.
|
|
102
|
+
|
|
103
|
+
:param child: The component to be updated.
|
|
104
|
+
:param url: The URL from which new data will be fetched.
|
|
105
|
+
:param interval: The time interval (in milliseconds) for polling updates. Default is 5000ms.
|
|
106
|
+
"""
|
|
107
|
+
super().__init__("div", child)
|
|
108
|
+
js = f"""
|
|
109
|
+
setInterval(() => fetchAndUpdate('{url}', '{{}}', '{child._id}'), {interval});
|
|
110
|
+
"""
|
|
111
|
+
self.append(script(js))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def new_id():
|
|
115
|
+
"""
|
|
116
|
+
Generates a new unique identifier for reactive components.
|
|
117
|
+
"""
|
|
118
|
+
return "tlmg" + str(uuid.uuid4()).split("-")[0]
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def to_js_type(data: Any) -> str:
|
|
122
|
+
return json.dumps(data)
|