mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 18:27:29 -05:00
Bring back pyweb as it was (#2105)
This commit is contained in:
committed by
GitHub
parent
15c19aa708
commit
4b90ebdef5
4
pyscript.core/package-lock.json
generated
4
pyscript.core/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@pyscript/core",
|
"name": "@pyscript/core",
|
||||||
"version": "0.4.48",
|
"version": "0.4.50",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@pyscript/core",
|
"name": "@pyscript/core",
|
||||||
"version": "0.4.48",
|
"version": "0.4.50",
|
||||||
"license": "APACHE-2.0",
|
"license": "APACHE-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ungap/with-resolvers": "^0.1.0",
|
"@ungap/with-resolvers": "^0.1.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@pyscript/core",
|
"name": "@pyscript/core",
|
||||||
"version": "0.4.48",
|
"version": "0.4.50",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "PyScript",
|
"description": "PyScript",
|
||||||
"module": "./index.js",
|
"module": "./index.js",
|
||||||
|
|||||||
@@ -19,17 +19,16 @@ def when(event_type=None, selector=None):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
|
|
||||||
from pyscript.web.elements import Element, ElementCollection
|
|
||||||
|
|
||||||
if isinstance(selector, str):
|
if isinstance(selector, str):
|
||||||
elements = document.querySelectorAll(selector)
|
elements = document.querySelectorAll(selector)
|
||||||
else:
|
else:
|
||||||
# TODO: This is a hack that will be removed when pyscript becomes a package
|
# TODO: This is a hack that will be removed when pyscript becomes a package
|
||||||
# and we can better manage the imports without circular dependencies
|
# and we can better manage the imports without circular dependencies
|
||||||
if isinstance(selector, Element):
|
from pyweb import pydom
|
||||||
|
|
||||||
|
if isinstance(selector, pydom.Element):
|
||||||
elements = [selector._js]
|
elements = [selector._js]
|
||||||
elif isinstance(selector, ElementCollection):
|
elif isinstance(selector, pydom.ElementCollection):
|
||||||
elements = [el._js for el in selector]
|
elements = [el._js for el in selector]
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
|||||||
2
pyscript.core/src/stdlib/pyweb/__init__.py
Normal file
2
pyscript.core/src/stdlib/pyweb/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from .pydom import JSProperty
|
||||||
|
from .pydom import dom as pydom
|
||||||
95
pyscript.core/src/stdlib/pyweb/media.py
Normal file
95
pyscript.core/src/stdlib/pyweb/media.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
from pyodide.ffi import to_js
|
||||||
|
from pyscript import window
|
||||||
|
|
||||||
|
|
||||||
|
class Device:
|
||||||
|
"""Device represents a media input or output device, such as a microphone,
|
||||||
|
camera, or headset.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, device):
|
||||||
|
self._js = device
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self._js.deviceId
|
||||||
|
|
||||||
|
@property
|
||||||
|
def group(self):
|
||||||
|
return self._js.groupId
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kind(self):
|
||||||
|
return self._js.kind
|
||||||
|
|
||||||
|
@property
|
||||||
|
def label(self):
|
||||||
|
return self._js.label
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return getattr(self, key)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def load(cls, audio=False, video=True):
|
||||||
|
"""Load the device stream."""
|
||||||
|
options = window.Object.new()
|
||||||
|
options.audio = audio
|
||||||
|
if isinstance(video, bool):
|
||||||
|
options.video = video
|
||||||
|
else:
|
||||||
|
# TODO: Think this can be simplified but need to check it on the pyodide side
|
||||||
|
|
||||||
|
# TODO: this is pyodide specific. shouldn't be!
|
||||||
|
options.video = window.Object.new()
|
||||||
|
for k in video:
|
||||||
|
setattr(
|
||||||
|
options.video,
|
||||||
|
k,
|
||||||
|
to_js(video[k], dict_converter=window.Object.fromEntries),
|
||||||
|
)
|
||||||
|
|
||||||
|
stream = await window.navigator.mediaDevices.getUserMedia(options)
|
||||||
|
return stream
|
||||||
|
|
||||||
|
async def get_stream(self):
|
||||||
|
key = self.kind.replace("input", "").replace("output", "")
|
||||||
|
options = {key: {"deviceId": {"exact": self.id}}}
|
||||||
|
|
||||||
|
return await self.load(**options)
|
||||||
|
|
||||||
|
|
||||||
|
async def list_devices() -> list[dict]:
|
||||||
|
"""
|
||||||
|
Return the list of the currently available media input and output devices,
|
||||||
|
such as microphones, cameras, headsets, and so forth.
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
list(dict) - list of dictionaries representing the available media devices.
|
||||||
|
Each dictionary has the following keys:
|
||||||
|
* deviceId: a string that is an identifier for the represented device
|
||||||
|
that is persisted across sessions. It is un-guessable by other
|
||||||
|
applications and unique to the origin of the calling application.
|
||||||
|
It is reset when the user clears cookies (for Private Browsing, a
|
||||||
|
different identifier is used that is not persisted across sessions).
|
||||||
|
|
||||||
|
* groupId: a string that is a group identifier. Two devices have the same
|
||||||
|
group identifier if they belong to the same physical device — for
|
||||||
|
example a monitor with both a built-in camera and a microphone.
|
||||||
|
|
||||||
|
* kind: an enumerated value that is either "videoinput", "audioinput"
|
||||||
|
or "audiooutput".
|
||||||
|
|
||||||
|
* label: a string describing this device (for example "External USB
|
||||||
|
Webcam").
|
||||||
|
|
||||||
|
Note: the returned list will omit any devices that are blocked by the document
|
||||||
|
Permission Policy: microphone, camera, speaker-selection (for output devices),
|
||||||
|
and so on. Access to particular non-default devices is also gated by the
|
||||||
|
Permissions API, and the list will omit devices for which the user has not
|
||||||
|
granted explicit permission.
|
||||||
|
"""
|
||||||
|
# https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices
|
||||||
|
return [
|
||||||
|
Device(obj) for obj in await window.navigator.mediaDevices.enumerateDevices()
|
||||||
|
]
|
||||||
569
pyscript.core/src/stdlib/pyweb/pydom.py
Normal file
569
pyscript.core/src/stdlib/pyweb/pydom.py
Normal file
@@ -0,0 +1,569 @@
|
|||||||
|
import inspect
|
||||||
|
|
||||||
|
try:
|
||||||
|
from typing import Any
|
||||||
|
except ImportError:
|
||||||
|
Any = "Any"
|
||||||
|
|
||||||
|
try:
|
||||||
|
import warnings
|
||||||
|
except ImportError:
|
||||||
|
# TODO: For now it probably means we are in MicroPython. We should figure
|
||||||
|
# out the "right" way to handle this. For now we just ignore the warning
|
||||||
|
# and logging to console
|
||||||
|
class warnings:
|
||||||
|
@staticmethod
|
||||||
|
def warn(*args, **kwargs):
|
||||||
|
print("WARNING: ", *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from functools import cached_property
|
||||||
|
except ImportError:
|
||||||
|
# TODO: same comment about micropython as above
|
||||||
|
cached_property = property
|
||||||
|
|
||||||
|
try:
|
||||||
|
from pyodide.ffi import JsProxy
|
||||||
|
except ImportError:
|
||||||
|
# TODO: same comment about micropython as above
|
||||||
|
def JsProxy(obj):
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
from pyscript import display, document, window
|
||||||
|
|
||||||
|
alert = window.alert
|
||||||
|
|
||||||
|
|
||||||
|
class JSProperty:
|
||||||
|
"""JS property descriptor that directly maps to the property with the same
|
||||||
|
name in the underlying JS component."""
|
||||||
|
|
||||||
|
def __init__(self, name: str, allow_nones: bool = False):
|
||||||
|
self.name = name
|
||||||
|
self.allow_nones = allow_nones
|
||||||
|
|
||||||
|
def __get__(self, obj, objtype=None):
|
||||||
|
return getattr(obj._js, self.name)
|
||||||
|
|
||||||
|
def __set__(self, obj, value):
|
||||||
|
if not self.allow_nones and value is None:
|
||||||
|
return
|
||||||
|
setattr(obj._js, self.name, value)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseElement:
|
||||||
|
def __init__(self, js_element):
|
||||||
|
self._js = js_element
|
||||||
|
self._parent = None
|
||||||
|
self.style = StyleProxy(self)
|
||||||
|
self._proxies = {}
|
||||||
|
|
||||||
|
def __eq__(self, obj):
|
||||||
|
"""Check if the element is the same as the other element by comparing
|
||||||
|
the underlying JS element"""
|
||||||
|
return isinstance(obj, BaseElement) and obj._js == self._js
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parent(self):
|
||||||
|
if self._parent:
|
||||||
|
return self._parent
|
||||||
|
|
||||||
|
if self._js.parentElement:
|
||||||
|
self._parent = self.__class__(self._js.parentElement)
|
||||||
|
|
||||||
|
return self._parent
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __class(self):
|
||||||
|
return self.__class__ if self.__class__ != PyDom else Element
|
||||||
|
|
||||||
|
def create(self, type_, is_child=True, classes=None, html=None, label=None):
|
||||||
|
js_el = document.createElement(type_)
|
||||||
|
element = self.__class(js_el)
|
||||||
|
|
||||||
|
if classes:
|
||||||
|
for class_ in classes:
|
||||||
|
element.add_class(class_)
|
||||||
|
|
||||||
|
if html is not None:
|
||||||
|
element.html = html
|
||||||
|
|
||||||
|
if label is not None:
|
||||||
|
element.label = label
|
||||||
|
|
||||||
|
if is_child:
|
||||||
|
self.append(element)
|
||||||
|
|
||||||
|
return element
|
||||||
|
|
||||||
|
def find(self, selector):
|
||||||
|
"""Return an ElementCollection representing all the child elements that
|
||||||
|
match the specified selector.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
selector (str): A string containing a selector expression
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ElementCollection: A collection of elements matching the selector
|
||||||
|
"""
|
||||||
|
elements = self._js.querySelectorAll(selector)
|
||||||
|
if not elements:
|
||||||
|
return None
|
||||||
|
return ElementCollection([Element(el) for el in elements])
|
||||||
|
|
||||||
|
|
||||||
|
class Element(BaseElement):
|
||||||
|
@property
|
||||||
|
def children(self):
|
||||||
|
return [self.__class__(el) for el in self._js.children]
|
||||||
|
|
||||||
|
def append(self, child):
|
||||||
|
# TODO: this is Pyodide specific for now!!!!!!
|
||||||
|
# if we get passed a JSProxy Element directly we just map it to the
|
||||||
|
# higher level Python element
|
||||||
|
if inspect.isclass(JsProxy) and isinstance(child, JsProxy):
|
||||||
|
return self.append(Element(child))
|
||||||
|
|
||||||
|
elif isinstance(child, Element):
|
||||||
|
self._js.appendChild(child._js)
|
||||||
|
|
||||||
|
return child
|
||||||
|
|
||||||
|
elif isinstance(child, ElementCollection):
|
||||||
|
for el in child:
|
||||||
|
self.append(el)
|
||||||
|
|
||||||
|
# -------- Pythonic Interface to Element -------- #
|
||||||
|
@property
|
||||||
|
def html(self):
|
||||||
|
return self._js.innerHTML
|
||||||
|
|
||||||
|
@html.setter
|
||||||
|
def html(self, value):
|
||||||
|
self._js.innerHTML = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def text(self):
|
||||||
|
return self._js.textContent
|
||||||
|
|
||||||
|
@text.setter
|
||||||
|
def text(self, value):
|
||||||
|
self._js.textContent = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def content(self):
|
||||||
|
# TODO: This breaks with with standard template elements. Define how to best
|
||||||
|
# handle this specifica use case. Just not support for now?
|
||||||
|
if self._js.tagName == "TEMPLATE":
|
||||||
|
warnings.warn(
|
||||||
|
"Content attribute not supported for template elements.", stacklevel=2
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
return self._js.innerHTML
|
||||||
|
|
||||||
|
@content.setter
|
||||||
|
def content(self, value):
|
||||||
|
# TODO: (same comment as above)
|
||||||
|
if self._js.tagName == "TEMPLATE":
|
||||||
|
warnings.warn(
|
||||||
|
"Content attribute not supported for template elements.", stacklevel=2
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
display(value, target=self.id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self._js.id
|
||||||
|
|
||||||
|
@id.setter
|
||||||
|
def id(self, value):
|
||||||
|
self._js.id = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def options(self):
|
||||||
|
if "options" in self._proxies:
|
||||||
|
return self._proxies["options"]
|
||||||
|
|
||||||
|
if not self._js.tagName.lower() in {"select", "datalist", "optgroup"}:
|
||||||
|
raise AttributeError(
|
||||||
|
f"Element {self._js.tagName} has no options attribute."
|
||||||
|
)
|
||||||
|
self._proxies["options"] = OptionsProxy(self)
|
||||||
|
return self._proxies["options"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
return self._js.value
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, value):
|
||||||
|
# in order to avoid confusion to the user, we don't allow setting the
|
||||||
|
# value of elements that don't have a value attribute
|
||||||
|
if not hasattr(self._js, "value"):
|
||||||
|
raise AttributeError(
|
||||||
|
f"Element {self._js.tagName} has no value attribute. If you want to "
|
||||||
|
"force a value attribute, set it directly using the `_js.value = <value>` "
|
||||||
|
"javascript API attribute instead."
|
||||||
|
)
|
||||||
|
self._js.value = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def selected(self):
|
||||||
|
return self._js.selected
|
||||||
|
|
||||||
|
@selected.setter
|
||||||
|
def selected(self, value):
|
||||||
|
# in order to avoid confusion to the user, we don't allow setting the
|
||||||
|
# value of elements that don't have a value attribute
|
||||||
|
if not hasattr(self._js, "selected"):
|
||||||
|
raise AttributeError(
|
||||||
|
f"Element {self._js.tagName} has no value attribute. If you want to "
|
||||||
|
"force a value attribute, set it directly using the `_js.value = <value>` "
|
||||||
|
"javascript API attribute instead."
|
||||||
|
)
|
||||||
|
self._js.selected = value
|
||||||
|
|
||||||
|
def clone(self, new_id=None):
|
||||||
|
clone = Element(self._js.cloneNode(True))
|
||||||
|
clone.id = new_id
|
||||||
|
|
||||||
|
return clone
|
||||||
|
|
||||||
|
def remove_class(self, classname):
|
||||||
|
classList = self._js.classList
|
||||||
|
if isinstance(classname, list):
|
||||||
|
classList.remove(*classname)
|
||||||
|
else:
|
||||||
|
classList.remove(classname)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add_class(self, classname):
|
||||||
|
classList = self._js.classList
|
||||||
|
if isinstance(classname, list):
|
||||||
|
classList.add(*classname)
|
||||||
|
else:
|
||||||
|
self._js.classList.add(classname)
|
||||||
|
return self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def classes(self):
|
||||||
|
classes = self._js.classList.values()
|
||||||
|
return [x for x in classes]
|
||||||
|
|
||||||
|
def show_me(self):
|
||||||
|
self._js.scrollIntoView()
|
||||||
|
|
||||||
|
def snap(
|
||||||
|
self,
|
||||||
|
to: BaseElement | str = None,
|
||||||
|
width: int | None = None,
|
||||||
|
height: int | None = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Captures a snapshot of a video element. (Only available for video elements)
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
|
||||||
|
* to: element where to save the snapshot of the video frame to
|
||||||
|
* width: width of the image
|
||||||
|
* height: height of the image
|
||||||
|
|
||||||
|
Output:
|
||||||
|
(Element) canvas element where the video frame snapshot was drawn into
|
||||||
|
"""
|
||||||
|
if self._js.tagName != "VIDEO":
|
||||||
|
raise AttributeError("Snap method is only available for video Elements")
|
||||||
|
|
||||||
|
if to is None:
|
||||||
|
canvas = self.create("canvas")
|
||||||
|
if width is None:
|
||||||
|
width = self._js.width
|
||||||
|
if height is None:
|
||||||
|
height = self._js.height
|
||||||
|
canvas._js.width = width
|
||||||
|
canvas._js.height = height
|
||||||
|
|
||||||
|
elif isinstance(to, Element):
|
||||||
|
if to._js.tagName != "CANVAS":
|
||||||
|
raise TypeError("Element to snap to must a canvas.")
|
||||||
|
canvas = to
|
||||||
|
elif getattr(to, "tagName", "") == "CANVAS":
|
||||||
|
canvas = Element(to)
|
||||||
|
elif isinstance(to, str):
|
||||||
|
canvas = pydom[to][0]
|
||||||
|
if canvas._js.tagName != "CANVAS":
|
||||||
|
raise TypeError("Element to snap to must a be canvas.")
|
||||||
|
|
||||||
|
canvas.draw(self, width, height)
|
||||||
|
|
||||||
|
return canvas
|
||||||
|
|
||||||
|
def download(self, filename: str = "snapped.png") -> None:
|
||||||
|
"""Download the current element (only available for canvas elements) with the filename
|
||||||
|
provided in input.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
* filename (str): name of the file being downloaded
|
||||||
|
|
||||||
|
Output:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
if self._js.tagName != "CANVAS":
|
||||||
|
raise AttributeError(
|
||||||
|
"The download method is only available for canvas Elements"
|
||||||
|
)
|
||||||
|
|
||||||
|
link = self.create("a")
|
||||||
|
link._js.download = filename
|
||||||
|
link._js.href = self._js.toDataURL()
|
||||||
|
link._js.click()
|
||||||
|
|
||||||
|
def draw(self, what, width, height):
|
||||||
|
"""Draw `what` on the current element (only available for canvas elements).
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
|
||||||
|
* what (canvas image source): An element to draw into the context. The specification permits any canvas
|
||||||
|
image source, specifically, an HTMLImageElement, an SVGImageElement, an HTMLVideoElement,
|
||||||
|
an HTMLCanvasElement, an ImageBitmap, an OffscreenCanvas, or a VideoFrame.
|
||||||
|
"""
|
||||||
|
if self._js.tagName != "CANVAS":
|
||||||
|
raise AttributeError(
|
||||||
|
"The draw method is only available for canvas Elements"
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(what, Element):
|
||||||
|
what = what._js
|
||||||
|
|
||||||
|
# https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
|
||||||
|
self._js.getContext("2d").drawImage(what, 0, 0, width, height)
|
||||||
|
|
||||||
|
|
||||||
|
class OptionsProxy:
|
||||||
|
"""This class represents the options of a select element. It
|
||||||
|
allows to access to add and remove options by using the `add` and `remove` methods.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, element: Element) -> None:
|
||||||
|
self._element = element
|
||||||
|
if self._element._js.tagName.lower() != "select":
|
||||||
|
raise AttributeError(
|
||||||
|
f"Element {self._element._js.tagName} has no options attribute."
|
||||||
|
)
|
||||||
|
|
||||||
|
def add(
|
||||||
|
self,
|
||||||
|
value: Any = None,
|
||||||
|
html: str = None,
|
||||||
|
text: str = None,
|
||||||
|
before: Element | int = None,
|
||||||
|
**kws,
|
||||||
|
) -> None:
|
||||||
|
"""Add a new option to the select element"""
|
||||||
|
# create the option element and set the attributes
|
||||||
|
option = document.createElement("option")
|
||||||
|
if value is not None:
|
||||||
|
kws["value"] = value
|
||||||
|
if html is not None:
|
||||||
|
option.innerHTML = html
|
||||||
|
if text is not None:
|
||||||
|
kws["text"] = text
|
||||||
|
|
||||||
|
for key, value in kws.items():
|
||||||
|
option.setAttribute(key, value)
|
||||||
|
|
||||||
|
if before:
|
||||||
|
if isinstance(before, Element):
|
||||||
|
before = before._js
|
||||||
|
|
||||||
|
self._element._js.add(option, before)
|
||||||
|
|
||||||
|
def remove(self, item: int) -> None:
|
||||||
|
"""Remove the option at the specified index"""
|
||||||
|
self._element._js.remove(item)
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
"""Remove all the options"""
|
||||||
|
for i in range(len(self)):
|
||||||
|
self.remove(0)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def options(self):
|
||||||
|
"""Return the list of options"""
|
||||||
|
return [Element(opt) for opt in self._element._js.options]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def selected(self):
|
||||||
|
"""Return the selected option"""
|
||||||
|
return self.options[self._element._js.selectedIndex]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
yield from self.options
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.options)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"{self.__class__.__name__} (length: {len(self)}) {self.options}"
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return self.options[key]
|
||||||
|
|
||||||
|
|
||||||
|
class StyleProxy: # (dict):
|
||||||
|
def __init__(self, element: Element) -> None:
|
||||||
|
self._element = element
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def _style(self):
|
||||||
|
return self._element._js.style
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return self._style.getPropertyValue(key)
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
self._style.setProperty(key, value)
|
||||||
|
|
||||||
|
def remove(self, key):
|
||||||
|
self._style.removeProperty(key)
|
||||||
|
|
||||||
|
def set(self, **kws):
|
||||||
|
for k, v in kws.items():
|
||||||
|
self._element._js.style.setProperty(k, v)
|
||||||
|
|
||||||
|
# CSS Properties
|
||||||
|
# Reference: https://github.com/microsoft/TypeScript/blob/main/src/lib/dom.generated.d.ts#L3799C1-L5005C2
|
||||||
|
# Following prperties automatically generated from the above reference using
|
||||||
|
# tools/codegen_css_proxy.py
|
||||||
|
@property
|
||||||
|
def visible(self):
|
||||||
|
return self._element._js.style.visibility
|
||||||
|
|
||||||
|
@visible.setter
|
||||||
|
def visible(self, value):
|
||||||
|
self._element._js.style.visibility = value
|
||||||
|
|
||||||
|
|
||||||
|
class StyleCollection:
|
||||||
|
def __init__(self, collection: "ElementCollection") -> None:
|
||||||
|
self._collection = collection
|
||||||
|
|
||||||
|
def __get__(self, obj, objtype=None):
|
||||||
|
return obj._get_attribute("style")
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return self._collection._get_attribute("style")[key]
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
for element in self._collection._elements:
|
||||||
|
element.style[key] = value
|
||||||
|
|
||||||
|
def remove(self, key):
|
||||||
|
for element in self._collection._elements:
|
||||||
|
element.style.remove(key)
|
||||||
|
|
||||||
|
|
||||||
|
class ElementCollection:
|
||||||
|
def __init__(self, elements: [Element]) -> None:
|
||||||
|
self._elements = elements
|
||||||
|
self.style = StyleCollection(self)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
# If it's an integer we use it to access the elements in the collection
|
||||||
|
if isinstance(key, int):
|
||||||
|
return self._elements[key]
|
||||||
|
# If it's a slice we use it to support slice operations over the elements
|
||||||
|
# in the collection
|
||||||
|
elif isinstance(key, slice):
|
||||||
|
return ElementCollection(self._elements[key])
|
||||||
|
|
||||||
|
# If it's anything else (basically a string) we use it as a selector
|
||||||
|
# TODO: Write tests!
|
||||||
|
elements = self._element.querySelectorAll(key)
|
||||||
|
return ElementCollection([Element(el) for el in elements])
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._elements)
|
||||||
|
|
||||||
|
def __eq__(self, obj):
|
||||||
|
"""Check if the element is the same as the other element by comparing
|
||||||
|
the underlying JS element"""
|
||||||
|
return isinstance(obj, ElementCollection) and obj._elements == self._elements
|
||||||
|
|
||||||
|
def _get_attribute(self, attr, index=None):
|
||||||
|
if index is None:
|
||||||
|
return [getattr(el, attr) for el in self._elements]
|
||||||
|
|
||||||
|
# As JQuery, when getting an attr, only return it for the first element
|
||||||
|
return getattr(self._elements[index], attr)
|
||||||
|
|
||||||
|
def _set_attribute(self, attr, value):
|
||||||
|
for el in self._elements:
|
||||||
|
setattr(el, attr, value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def html(self):
|
||||||
|
return self._get_attribute("html")
|
||||||
|
|
||||||
|
@html.setter
|
||||||
|
def html(self, value):
|
||||||
|
self._set_attribute("html", value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
return self._get_attribute("value")
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, value):
|
||||||
|
self._set_attribute("value", value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def children(self):
|
||||||
|
return self._elements
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
yield from self._elements
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"{self.__class__.__name__} (length: {len(self._elements)}) {self._elements}"
|
||||||
|
|
||||||
|
|
||||||
|
class DomScope:
|
||||||
|
def __getattr__(self, __name: str):
|
||||||
|
element = document[f"#{__name}"]
|
||||||
|
if element:
|
||||||
|
return element[0]
|
||||||
|
|
||||||
|
|
||||||
|
class PyDom(BaseElement):
|
||||||
|
# Add objects we want to expose to the DOM namespace since this class instance is being
|
||||||
|
# remapped as "the module" itself
|
||||||
|
BaseElement = BaseElement
|
||||||
|
Element = Element
|
||||||
|
ElementCollection = ElementCollection
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# PyDom is a special case of BaseElement where we don't want to create a new JS element
|
||||||
|
# and it really doesn't have a need for styleproxy or parent to to call to __init__
|
||||||
|
# (which actually fails in MP for some reason)
|
||||||
|
self._js = document
|
||||||
|
self._parent = None
|
||||||
|
self._proxies = {}
|
||||||
|
self.ids = DomScope()
|
||||||
|
self.body = Element(document.body)
|
||||||
|
self.head = Element(document.head)
|
||||||
|
|
||||||
|
def create(self, type_, classes=None, html=None):
|
||||||
|
return super().create(type_, is_child=False, classes=classes, html=html)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
elements = self._js.querySelectorAll(key)
|
||||||
|
if not elements:
|
||||||
|
return None
|
||||||
|
return ElementCollection([Element(el) for el in elements])
|
||||||
|
|
||||||
|
|
||||||
|
dom = PyDom()
|
||||||
1
pyscript.core/src/stdlib/pyweb/ui/__init__.py
Normal file
1
pyscript.core/src/stdlib/pyweb/ui/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import elements
|
||||||
947
pyscript.core/src/stdlib/pyweb/ui/elements.py
Normal file
947
pyscript.core/src/stdlib/pyweb/ui/elements.py
Normal file
@@ -0,0 +1,947 @@
|
|||||||
|
import inspect
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from pyscript import document, when, window
|
||||||
|
from pyweb import JSProperty, pydom
|
||||||
|
|
||||||
|
#: A flag to show if MicroPython is the current Python interpreter.
|
||||||
|
is_micropython = "MicroPython" in sys.version
|
||||||
|
|
||||||
|
|
||||||
|
def getmembers_static(cls):
|
||||||
|
"""Cross-interpreter implementation of inspect.getmembers_static."""
|
||||||
|
|
||||||
|
if is_micropython: # pragma: no cover
|
||||||
|
return [(name, getattr(cls, name)) for name, _ in inspect.getmembers(cls)]
|
||||||
|
|
||||||
|
return inspect.getmembers_static(cls)
|
||||||
|
|
||||||
|
|
||||||
|
class ElementBase(pydom.Element):
|
||||||
|
tag = "div"
|
||||||
|
|
||||||
|
# GLOBAL ATTRIBUTES
|
||||||
|
# These are attribute that all elements have (this list is a subset of the official one)
|
||||||
|
# We are trying to capture the most used ones
|
||||||
|
accesskey = JSProperty("accesskey")
|
||||||
|
autofocus = JSProperty("autofocus")
|
||||||
|
autocapitalize = JSProperty("autocapitalize")
|
||||||
|
className = JSProperty("className")
|
||||||
|
contenteditable = JSProperty("contenteditable")
|
||||||
|
draggable = JSProperty("draggable")
|
||||||
|
enterkeyhint = JSProperty("enterkeyhint")
|
||||||
|
hidden = JSProperty("hidden")
|
||||||
|
id = JSProperty("id")
|
||||||
|
lang = JSProperty("lang")
|
||||||
|
nonce = JSProperty("nonce")
|
||||||
|
part = JSProperty("part")
|
||||||
|
popover = JSProperty("popover")
|
||||||
|
slot = JSProperty("slot")
|
||||||
|
spellcheck = JSProperty("spellcheck")
|
||||||
|
tabindex = JSProperty("tabindex")
|
||||||
|
title = JSProperty("title")
|
||||||
|
translate = JSProperty("translate")
|
||||||
|
virtualkeyboardpolicy = JSProperty("virtualkeyboardpolicy")
|
||||||
|
|
||||||
|
def __init__(self, style=None, **kwargs):
|
||||||
|
super().__init__(document.createElement(self.tag))
|
||||||
|
|
||||||
|
# set all the style properties provided in input
|
||||||
|
if isinstance(style, dict):
|
||||||
|
for key, value in style.items():
|
||||||
|
self.style[key] = value
|
||||||
|
elif style is None:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Style should be a dictionary, received {style} (type {type(style)}) instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
# IMPORTANT!!! This is used to auto-harvest all input arguments and set them as properties
|
||||||
|
self._init_properties(**kwargs)
|
||||||
|
|
||||||
|
def _init_properties(self, **kwargs):
|
||||||
|
"""Set all the properties (of type JSProperties) provided in input as properties
|
||||||
|
of the class instance.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
**kwargs: The properties to set
|
||||||
|
"""
|
||||||
|
# Look at all the properties of the class and see if they were provided in kwargs
|
||||||
|
for attr_name, attr in getmembers_static(self.__class__):
|
||||||
|
# For each one, actually check if it is a property of the class and set it
|
||||||
|
if isinstance(attr, JSProperty) and attr_name in kwargs:
|
||||||
|
try:
|
||||||
|
setattr(self, attr_name, kwargs[attr_name])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error setting {attr_name} to {kwargs[attr_name]}: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class TextElementBase(ElementBase):
|
||||||
|
def __init__(self, content=None, style=None, **kwargs):
|
||||||
|
super().__init__(style=style, **kwargs)
|
||||||
|
|
||||||
|
# If it's an element, append the element
|
||||||
|
if isinstance(content, pydom.Element):
|
||||||
|
self.append(content)
|
||||||
|
# If it's a list of elements
|
||||||
|
elif isinstance(content, list):
|
||||||
|
for item in content:
|
||||||
|
self.append(item)
|
||||||
|
# If the content wasn't set just ignore
|
||||||
|
elif content is None:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# Otherwise, set content as the html of the element
|
||||||
|
self.html = content
|
||||||
|
|
||||||
|
|
||||||
|
# IMPORTANT: For all HTML components defined below, we are not mapping all
|
||||||
|
# available attributes, just the global and the most common ones.
|
||||||
|
# If you need to access a specific attribute, you can always use the `_js.<attribute>`
|
||||||
|
class a(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a"""
|
||||||
|
|
||||||
|
tag = "a"
|
||||||
|
|
||||||
|
download = JSProperty("download")
|
||||||
|
href = JSProperty("href")
|
||||||
|
referrerpolicy = JSProperty("referrerpolicy")
|
||||||
|
rel = JSProperty("rel")
|
||||||
|
target = JSProperty("target")
|
||||||
|
type = JSProperty("type")
|
||||||
|
|
||||||
|
|
||||||
|
class abbr(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr"""
|
||||||
|
|
||||||
|
tag = "abbr"
|
||||||
|
|
||||||
|
|
||||||
|
class address(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address"""
|
||||||
|
|
||||||
|
tag = "address"
|
||||||
|
|
||||||
|
|
||||||
|
class area(ElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area"""
|
||||||
|
|
||||||
|
tag = "area"
|
||||||
|
|
||||||
|
alt = JSProperty("alt")
|
||||||
|
coords = JSProperty("coords")
|
||||||
|
download = JSProperty("download")
|
||||||
|
href = JSProperty("href")
|
||||||
|
ping = JSProperty("ping")
|
||||||
|
referrerpolicy = JSProperty("referrerpolicy")
|
||||||
|
rel = JSProperty("rel")
|
||||||
|
shape = JSProperty("shape")
|
||||||
|
target = JSProperty("target")
|
||||||
|
|
||||||
|
|
||||||
|
class article(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article"""
|
||||||
|
|
||||||
|
tag = "article"
|
||||||
|
|
||||||
|
|
||||||
|
class aside(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside"""
|
||||||
|
|
||||||
|
tag = "aside"
|
||||||
|
|
||||||
|
|
||||||
|
class audio(ElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio"""
|
||||||
|
|
||||||
|
tag = "audio"
|
||||||
|
|
||||||
|
autoplay = JSProperty("autoplay")
|
||||||
|
controls = JSProperty("controls")
|
||||||
|
controlslist = JSProperty("controlslist")
|
||||||
|
crossorigin = JSProperty("crossorigin")
|
||||||
|
disableremoteplayback = JSProperty("disableremoteplayback")
|
||||||
|
loop = JSProperty("loop")
|
||||||
|
muted = JSProperty("muted")
|
||||||
|
preload = JSProperty("preload")
|
||||||
|
src = JSProperty("src")
|
||||||
|
|
||||||
|
|
||||||
|
class b(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b"""
|
||||||
|
|
||||||
|
tag = "b"
|
||||||
|
|
||||||
|
|
||||||
|
class blockquote(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote"""
|
||||||
|
|
||||||
|
tag = "blockquote"
|
||||||
|
|
||||||
|
cite = JSProperty("cite")
|
||||||
|
|
||||||
|
|
||||||
|
class br(ElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br"""
|
||||||
|
|
||||||
|
tag = "br"
|
||||||
|
|
||||||
|
|
||||||
|
class button(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button"""
|
||||||
|
|
||||||
|
tag = "button"
|
||||||
|
|
||||||
|
autofocus = JSProperty("autofocus")
|
||||||
|
disabled = JSProperty("disabled")
|
||||||
|
form = JSProperty("form")
|
||||||
|
formaction = JSProperty("formaction")
|
||||||
|
formenctype = JSProperty("formenctype")
|
||||||
|
formmethod = JSProperty("formmethod")
|
||||||
|
formnovalidate = JSProperty("formnovalidate")
|
||||||
|
formtarget = JSProperty("formtarget")
|
||||||
|
name = JSProperty("name")
|
||||||
|
type = JSProperty("type")
|
||||||
|
value = JSProperty("value")
|
||||||
|
|
||||||
|
|
||||||
|
class canvas(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas"""
|
||||||
|
|
||||||
|
tag = "canvas"
|
||||||
|
|
||||||
|
height = JSProperty("height")
|
||||||
|
width = JSProperty("width")
|
||||||
|
|
||||||
|
|
||||||
|
class caption(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption"""
|
||||||
|
|
||||||
|
tag = "caption"
|
||||||
|
|
||||||
|
|
||||||
|
class cite(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite"""
|
||||||
|
|
||||||
|
tag = "cite"
|
||||||
|
|
||||||
|
|
||||||
|
class code(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code"""
|
||||||
|
|
||||||
|
tag = "code"
|
||||||
|
|
||||||
|
|
||||||
|
class data(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data"""
|
||||||
|
|
||||||
|
tag = "data"
|
||||||
|
|
||||||
|
value = JSProperty("value")
|
||||||
|
|
||||||
|
|
||||||
|
class datalist(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist"""
|
||||||
|
|
||||||
|
tag = "datalist"
|
||||||
|
|
||||||
|
|
||||||
|
class dd(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd"""
|
||||||
|
|
||||||
|
tag = "dd"
|
||||||
|
|
||||||
|
|
||||||
|
class del_(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del"""
|
||||||
|
|
||||||
|
tag = "del"
|
||||||
|
|
||||||
|
cite = JSProperty("cite")
|
||||||
|
datetime = JSProperty("datetime")
|
||||||
|
|
||||||
|
|
||||||
|
class details(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details"""
|
||||||
|
|
||||||
|
tag = "details"
|
||||||
|
|
||||||
|
open = JSProperty("open")
|
||||||
|
|
||||||
|
|
||||||
|
class dialog(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog"""
|
||||||
|
|
||||||
|
tag = "dialog"
|
||||||
|
|
||||||
|
open = JSProperty("open")
|
||||||
|
|
||||||
|
|
||||||
|
class div(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div"""
|
||||||
|
|
||||||
|
tag = "div"
|
||||||
|
|
||||||
|
|
||||||
|
class dl(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl"""
|
||||||
|
|
||||||
|
tag = "dl"
|
||||||
|
|
||||||
|
value = JSProperty("value")
|
||||||
|
|
||||||
|
|
||||||
|
class dt(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt"""
|
||||||
|
|
||||||
|
tag = "dt"
|
||||||
|
|
||||||
|
|
||||||
|
class em(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em"""
|
||||||
|
|
||||||
|
tag = "em"
|
||||||
|
|
||||||
|
|
||||||
|
class embed(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed"""
|
||||||
|
|
||||||
|
tag = "embed"
|
||||||
|
|
||||||
|
height = JSProperty("height")
|
||||||
|
src = JSProperty("src")
|
||||||
|
type = JSProperty("type")
|
||||||
|
width = JSProperty("width")
|
||||||
|
|
||||||
|
|
||||||
|
class fieldset(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset"""
|
||||||
|
|
||||||
|
tag = "fieldset"
|
||||||
|
|
||||||
|
disabled = JSProperty("disabled")
|
||||||
|
form = JSProperty("form")
|
||||||
|
name = JSProperty("name")
|
||||||
|
|
||||||
|
|
||||||
|
class figcaption(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption"""
|
||||||
|
|
||||||
|
tag = "figcaption"
|
||||||
|
|
||||||
|
|
||||||
|
class figure(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure"""
|
||||||
|
|
||||||
|
tag = "figure"
|
||||||
|
|
||||||
|
|
||||||
|
class footer(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer"""
|
||||||
|
|
||||||
|
tag = "footer"
|
||||||
|
|
||||||
|
|
||||||
|
class form(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form"""
|
||||||
|
|
||||||
|
tag = "form"
|
||||||
|
|
||||||
|
accept_charset = JSProperty("accept-charset")
|
||||||
|
action = JSProperty("action")
|
||||||
|
autocapitalize = JSProperty("autocapitalize")
|
||||||
|
autocomplete = JSProperty("autocomplete")
|
||||||
|
enctype = JSProperty("enctype")
|
||||||
|
name = JSProperty("name")
|
||||||
|
method = JSProperty("method")
|
||||||
|
nonvalidate = JSProperty("nonvalidate")
|
||||||
|
rel = JSProperty("rel")
|
||||||
|
target = JSProperty("target")
|
||||||
|
|
||||||
|
|
||||||
|
class h1(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1"""
|
||||||
|
|
||||||
|
tag = "h1"
|
||||||
|
|
||||||
|
|
||||||
|
class h2(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h2"""
|
||||||
|
|
||||||
|
tag = "h2"
|
||||||
|
|
||||||
|
|
||||||
|
class h3(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h3"""
|
||||||
|
|
||||||
|
tag = "h3"
|
||||||
|
|
||||||
|
|
||||||
|
class h4(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4"""
|
||||||
|
|
||||||
|
tag = "h4"
|
||||||
|
|
||||||
|
|
||||||
|
class h5(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h5"""
|
||||||
|
|
||||||
|
tag = "h5"
|
||||||
|
|
||||||
|
|
||||||
|
class h6(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6"""
|
||||||
|
|
||||||
|
tag = "h6"
|
||||||
|
|
||||||
|
|
||||||
|
class header(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header"""
|
||||||
|
|
||||||
|
tag = "header"
|
||||||
|
|
||||||
|
|
||||||
|
class hgroup(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hgroup"""
|
||||||
|
|
||||||
|
tag = "hgroup"
|
||||||
|
|
||||||
|
|
||||||
|
class hr(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr"""
|
||||||
|
|
||||||
|
tag = "hr"
|
||||||
|
|
||||||
|
|
||||||
|
class i(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i"""
|
||||||
|
|
||||||
|
tag = "i"
|
||||||
|
|
||||||
|
|
||||||
|
class iframe(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe"""
|
||||||
|
|
||||||
|
tag = "iframe"
|
||||||
|
|
||||||
|
allow = JSProperty("allow")
|
||||||
|
allowfullscreen = JSProperty("allowfullscreen")
|
||||||
|
height = JSProperty("height")
|
||||||
|
loading = JSProperty("loading")
|
||||||
|
name = JSProperty("name")
|
||||||
|
referrerpolicy = JSProperty("referrerpolicy")
|
||||||
|
sandbox = JSProperty("sandbox")
|
||||||
|
src = JSProperty("src")
|
||||||
|
srcdoc = JSProperty("srcdoc")
|
||||||
|
width = JSProperty("width")
|
||||||
|
|
||||||
|
|
||||||
|
class img(ElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img"""
|
||||||
|
|
||||||
|
tag = "img"
|
||||||
|
|
||||||
|
alt = JSProperty("alt")
|
||||||
|
crossorigin = JSProperty("crossorigin")
|
||||||
|
decoding = JSProperty("decoding")
|
||||||
|
fetchpriority = JSProperty("fetchpriority")
|
||||||
|
height = JSProperty("height")
|
||||||
|
ismap = JSProperty("ismap")
|
||||||
|
loading = JSProperty("loading")
|
||||||
|
referrerpolicy = JSProperty("referrerpolicy")
|
||||||
|
sizes = JSProperty("sizes")
|
||||||
|
src = JSProperty("src")
|
||||||
|
width = JSProperty("width")
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: Input is a reserved keyword in Python, so we use input_ instead
|
||||||
|
class input_(ElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input"""
|
||||||
|
|
||||||
|
tag = "input"
|
||||||
|
|
||||||
|
accept = JSProperty("accept")
|
||||||
|
alt = JSProperty("alt")
|
||||||
|
autofocus = JSProperty("autofocus")
|
||||||
|
capture = JSProperty("capture")
|
||||||
|
checked = JSProperty("checked")
|
||||||
|
dirname = JSProperty("dirname")
|
||||||
|
disabled = JSProperty("disabled")
|
||||||
|
form = JSProperty("form")
|
||||||
|
formaction = JSProperty("formaction")
|
||||||
|
formenctype = JSProperty("formenctype")
|
||||||
|
formmethod = JSProperty("formmethod")
|
||||||
|
formnovalidate = JSProperty("formnovalidate")
|
||||||
|
formtarget = JSProperty("formtarget")
|
||||||
|
height = JSProperty("height")
|
||||||
|
list = JSProperty("list")
|
||||||
|
max = JSProperty("max")
|
||||||
|
maxlength = JSProperty("maxlength")
|
||||||
|
min = JSProperty("min")
|
||||||
|
minlength = JSProperty("minlength")
|
||||||
|
multiple = JSProperty("multiple")
|
||||||
|
name = JSProperty("name")
|
||||||
|
pattern = JSProperty("pattern")
|
||||||
|
placeholder = JSProperty("placeholder")
|
||||||
|
popovertarget = JSProperty("popovertarget")
|
||||||
|
popovertargetaction = JSProperty("popovertargetaction")
|
||||||
|
readonly = JSProperty("readonly")
|
||||||
|
required = JSProperty("required")
|
||||||
|
size = JSProperty("size")
|
||||||
|
src = JSProperty("src")
|
||||||
|
step = JSProperty("step")
|
||||||
|
type = JSProperty("type")
|
||||||
|
value = JSProperty("value")
|
||||||
|
width = JSProperty("width")
|
||||||
|
|
||||||
|
|
||||||
|
class ins(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins"""
|
||||||
|
|
||||||
|
tag = "ins"
|
||||||
|
|
||||||
|
cite = JSProperty("cite")
|
||||||
|
datetime = JSProperty("datetime")
|
||||||
|
|
||||||
|
|
||||||
|
class kbd(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd"""
|
||||||
|
|
||||||
|
tag = "kbd"
|
||||||
|
|
||||||
|
|
||||||
|
class label(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label"""
|
||||||
|
|
||||||
|
tag = "label"
|
||||||
|
|
||||||
|
for_ = JSProperty("for")
|
||||||
|
|
||||||
|
|
||||||
|
class legend(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend"""
|
||||||
|
|
||||||
|
tag = "legend"
|
||||||
|
|
||||||
|
|
||||||
|
class li(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li"""
|
||||||
|
|
||||||
|
tag = "li"
|
||||||
|
|
||||||
|
value = JSProperty("value")
|
||||||
|
|
||||||
|
|
||||||
|
class link(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link"""
|
||||||
|
|
||||||
|
tag = "link"
|
||||||
|
|
||||||
|
as_ = JSProperty("as")
|
||||||
|
crossorigin = JSProperty("crossorigin")
|
||||||
|
disabled = JSProperty("disabled")
|
||||||
|
fetchpriority = JSProperty("fetchpriority")
|
||||||
|
href = JSProperty("href")
|
||||||
|
imagesizes = JSProperty("imagesizes")
|
||||||
|
imagesrcset = JSProperty("imagesrcset")
|
||||||
|
integrity = JSProperty("integrity")
|
||||||
|
media = JSProperty("media")
|
||||||
|
rel = JSProperty("rel")
|
||||||
|
referrerpolicy = JSProperty("referrerpolicy")
|
||||||
|
sizes = JSProperty("sizes")
|
||||||
|
title = JSProperty("title")
|
||||||
|
type = JSProperty("type")
|
||||||
|
|
||||||
|
|
||||||
|
class main(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main"""
|
||||||
|
|
||||||
|
tag = "main"
|
||||||
|
|
||||||
|
|
||||||
|
class map_(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map"""
|
||||||
|
|
||||||
|
tag = "map"
|
||||||
|
|
||||||
|
name = JSProperty("name")
|
||||||
|
|
||||||
|
|
||||||
|
class mark(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark"""
|
||||||
|
|
||||||
|
tag = "mark"
|
||||||
|
|
||||||
|
|
||||||
|
class menu(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu"""
|
||||||
|
|
||||||
|
tag = "menu"
|
||||||
|
|
||||||
|
|
||||||
|
class meter(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter"""
|
||||||
|
|
||||||
|
tag = "meter"
|
||||||
|
|
||||||
|
form = JSProperty("form")
|
||||||
|
high = JSProperty("high")
|
||||||
|
low = JSProperty("low")
|
||||||
|
max = JSProperty("max")
|
||||||
|
min = JSProperty("min")
|
||||||
|
optimum = JSProperty("optimum")
|
||||||
|
value = JSProperty("value")
|
||||||
|
|
||||||
|
|
||||||
|
class nav(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav"""
|
||||||
|
|
||||||
|
tag = "nav"
|
||||||
|
|
||||||
|
|
||||||
|
class object_(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object"""
|
||||||
|
|
||||||
|
tag = "object"
|
||||||
|
|
||||||
|
data = JSProperty("data")
|
||||||
|
form = JSProperty("form")
|
||||||
|
height = JSProperty("height")
|
||||||
|
name = JSProperty("name")
|
||||||
|
type = JSProperty("type")
|
||||||
|
usemap = JSProperty("usemap")
|
||||||
|
width = JSProperty("width")
|
||||||
|
|
||||||
|
|
||||||
|
class ol(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol"""
|
||||||
|
|
||||||
|
tag = "ol"
|
||||||
|
|
||||||
|
reversed = JSProperty("reversed")
|
||||||
|
start = JSProperty("start")
|
||||||
|
type = JSProperty("type")
|
||||||
|
|
||||||
|
|
||||||
|
class optgroup(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup"""
|
||||||
|
|
||||||
|
tag = "optgroup"
|
||||||
|
|
||||||
|
disabled = JSProperty("disabled")
|
||||||
|
label = JSProperty("label")
|
||||||
|
|
||||||
|
|
||||||
|
class option(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option"""
|
||||||
|
|
||||||
|
tag = "option"
|
||||||
|
|
||||||
|
disabled = JSProperty("value")
|
||||||
|
label = JSProperty("label")
|
||||||
|
selected = JSProperty("selected")
|
||||||
|
value = JSProperty("value")
|
||||||
|
|
||||||
|
|
||||||
|
class output(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output"""
|
||||||
|
|
||||||
|
tag = "output"
|
||||||
|
|
||||||
|
for_ = JSProperty("for")
|
||||||
|
form = JSProperty("form")
|
||||||
|
name = JSProperty("name")
|
||||||
|
|
||||||
|
|
||||||
|
class p(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p"""
|
||||||
|
|
||||||
|
tag = "p"
|
||||||
|
|
||||||
|
|
||||||
|
class picture(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture"""
|
||||||
|
|
||||||
|
tag = "picture"
|
||||||
|
|
||||||
|
|
||||||
|
class pre(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre"""
|
||||||
|
|
||||||
|
tag = "pre"
|
||||||
|
|
||||||
|
|
||||||
|
class progress(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress"""
|
||||||
|
|
||||||
|
tag = "progress"
|
||||||
|
|
||||||
|
max = JSProperty("max")
|
||||||
|
value = JSProperty("value")
|
||||||
|
|
||||||
|
|
||||||
|
class q(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q"""
|
||||||
|
|
||||||
|
tag = "q"
|
||||||
|
|
||||||
|
cite = JSProperty("cite")
|
||||||
|
|
||||||
|
|
||||||
|
class s(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s"""
|
||||||
|
|
||||||
|
tag = "s"
|
||||||
|
|
||||||
|
|
||||||
|
class script(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script"""
|
||||||
|
|
||||||
|
tag = "script"
|
||||||
|
|
||||||
|
# Let's add async manually since it's a reserved keyword in Python
|
||||||
|
async_ = JSProperty("async")
|
||||||
|
blocking = JSProperty("blocking")
|
||||||
|
crossorigin = JSProperty("crossorigin")
|
||||||
|
defer = JSProperty("defer")
|
||||||
|
fetchpriority = JSProperty("fetchpriority")
|
||||||
|
integrity = JSProperty("integrity")
|
||||||
|
nomodule = JSProperty("nomodule")
|
||||||
|
nonce = JSProperty("nonce")
|
||||||
|
referrerpolicy = JSProperty("referrerpolicy")
|
||||||
|
src = JSProperty("src")
|
||||||
|
type = JSProperty("type")
|
||||||
|
|
||||||
|
|
||||||
|
class section(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section"""
|
||||||
|
|
||||||
|
tag = "section"
|
||||||
|
|
||||||
|
|
||||||
|
class select(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select"""
|
||||||
|
|
||||||
|
tag = "select"
|
||||||
|
|
||||||
|
|
||||||
|
class small(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small"""
|
||||||
|
|
||||||
|
tag = "small"
|
||||||
|
|
||||||
|
|
||||||
|
class source(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source"""
|
||||||
|
|
||||||
|
tag = "source"
|
||||||
|
|
||||||
|
media = JSProperty("media")
|
||||||
|
sizes = JSProperty("sizes")
|
||||||
|
src = JSProperty("src")
|
||||||
|
srcset = JSProperty("srcset")
|
||||||
|
type = JSProperty("type")
|
||||||
|
|
||||||
|
|
||||||
|
class span(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span"""
|
||||||
|
|
||||||
|
tag = "span"
|
||||||
|
|
||||||
|
|
||||||
|
class strong(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong"""
|
||||||
|
|
||||||
|
tag = "strong"
|
||||||
|
|
||||||
|
|
||||||
|
class style(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style"""
|
||||||
|
|
||||||
|
tag = "style"
|
||||||
|
|
||||||
|
blocking = JSProperty("blocking")
|
||||||
|
media = JSProperty("media")
|
||||||
|
nonce = JSProperty("nonce")
|
||||||
|
title = JSProperty("title")
|
||||||
|
|
||||||
|
|
||||||
|
class sub(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub"""
|
||||||
|
|
||||||
|
tag = "sub"
|
||||||
|
|
||||||
|
|
||||||
|
class summary(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary"""
|
||||||
|
|
||||||
|
tag = "summary"
|
||||||
|
|
||||||
|
|
||||||
|
class sup(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup"""
|
||||||
|
|
||||||
|
tag = "sup"
|
||||||
|
|
||||||
|
|
||||||
|
class table(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table"""
|
||||||
|
|
||||||
|
tag = "table"
|
||||||
|
|
||||||
|
|
||||||
|
class tbody(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody"""
|
||||||
|
|
||||||
|
tag = "tbody"
|
||||||
|
|
||||||
|
|
||||||
|
class td(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td"""
|
||||||
|
|
||||||
|
tag = "td"
|
||||||
|
|
||||||
|
colspan = JSProperty("colspan")
|
||||||
|
headers = JSProperty("headers")
|
||||||
|
rowspan = JSProperty("rowspan")
|
||||||
|
|
||||||
|
|
||||||
|
class template(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template"""
|
||||||
|
|
||||||
|
tag = "template"
|
||||||
|
|
||||||
|
shadowrootmode = JSProperty("shadowrootmode")
|
||||||
|
|
||||||
|
|
||||||
|
class textarea(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea"""
|
||||||
|
|
||||||
|
tag = "textarea"
|
||||||
|
|
||||||
|
autocapitalize = JSProperty("autocapitalize")
|
||||||
|
autocomplete = JSProperty("autocomplete")
|
||||||
|
autofocus = JSProperty("autofocus")
|
||||||
|
cols = JSProperty("cols")
|
||||||
|
dirname = JSProperty("dirname")
|
||||||
|
disabled = JSProperty("disabled")
|
||||||
|
form = JSProperty("form")
|
||||||
|
maxlength = JSProperty("maxlength")
|
||||||
|
minlength = JSProperty("minlength")
|
||||||
|
name = JSProperty("name")
|
||||||
|
placeholder = JSProperty("placeholder")
|
||||||
|
readonly = JSProperty("readonly")
|
||||||
|
required = JSProperty("required")
|
||||||
|
rows = JSProperty("rows")
|
||||||
|
spellcheck = JSProperty("spellcheck")
|
||||||
|
wrap = JSProperty("wrap")
|
||||||
|
|
||||||
|
|
||||||
|
class tfoot(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot"""
|
||||||
|
|
||||||
|
tag = "tfoot"
|
||||||
|
|
||||||
|
|
||||||
|
class th(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th"""
|
||||||
|
|
||||||
|
tag = "th"
|
||||||
|
|
||||||
|
|
||||||
|
class thead(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead"""
|
||||||
|
|
||||||
|
tag = "thead"
|
||||||
|
|
||||||
|
|
||||||
|
class time(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time"""
|
||||||
|
|
||||||
|
tag = "time"
|
||||||
|
|
||||||
|
datetime = JSProperty("datetime")
|
||||||
|
|
||||||
|
|
||||||
|
class title(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title"""
|
||||||
|
|
||||||
|
tag = "title"
|
||||||
|
|
||||||
|
|
||||||
|
class tr(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr"""
|
||||||
|
|
||||||
|
tag = "tr"
|
||||||
|
|
||||||
|
abbr = JSProperty("abbr")
|
||||||
|
colspan = JSProperty("colspan")
|
||||||
|
headers = JSProperty("headers")
|
||||||
|
rowspan = JSProperty("rowspan")
|
||||||
|
scope = JSProperty("scope")
|
||||||
|
|
||||||
|
|
||||||
|
class track(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track"""
|
||||||
|
|
||||||
|
tag = "track"
|
||||||
|
|
||||||
|
default = JSProperty("default")
|
||||||
|
kind = JSProperty("kind")
|
||||||
|
label = JSProperty("label")
|
||||||
|
src = JSProperty("src")
|
||||||
|
srclang = JSProperty("srclang")
|
||||||
|
|
||||||
|
|
||||||
|
class u(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u"""
|
||||||
|
|
||||||
|
tag = "u"
|
||||||
|
|
||||||
|
|
||||||
|
class ul(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul"""
|
||||||
|
|
||||||
|
tag = "ul"
|
||||||
|
|
||||||
|
|
||||||
|
class var(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var"""
|
||||||
|
|
||||||
|
tag = "var"
|
||||||
|
|
||||||
|
|
||||||
|
class video(TextElementBase):
|
||||||
|
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video"""
|
||||||
|
|
||||||
|
tag = "video"
|
||||||
|
|
||||||
|
autoplay = JSProperty("autoplay")
|
||||||
|
controls = JSProperty("controls")
|
||||||
|
crossorigin = JSProperty("crossorigin")
|
||||||
|
disablepictureinpicture = JSProperty("disablepictureinpicture")
|
||||||
|
disableremoteplayback = JSProperty("disableremoteplayback")
|
||||||
|
height = JSProperty("height")
|
||||||
|
loop = JSProperty("loop")
|
||||||
|
muted = JSProperty("muted")
|
||||||
|
playsinline = JSProperty("playsinline")
|
||||||
|
poster = JSProperty("poster")
|
||||||
|
preload = JSProperty("preload")
|
||||||
|
src = JSProperty("src")
|
||||||
|
width = JSProperty("width")
|
||||||
|
|
||||||
|
|
||||||
|
# Custom Elements
|
||||||
|
class grid(TextElementBase):
|
||||||
|
tag = "div"
|
||||||
|
|
||||||
|
def __init__(self, layout, content=None, gap=None, **kwargs):
|
||||||
|
super().__init__(content, **kwargs)
|
||||||
|
self.style["display"] = "grid"
|
||||||
|
self.style["grid-template-columns"] = layout
|
||||||
|
|
||||||
|
# TODO: This should be a property
|
||||||
|
if not gap is None:
|
||||||
|
self.style["gap"] = gap
|
||||||
@@ -4,7 +4,7 @@ import time
|
|||||||
from datetime import datetime as dt
|
from datetime import datetime as dt
|
||||||
|
|
||||||
from pyscript import display, when
|
from pyscript import display, when
|
||||||
from pyscript.web import dom
|
from pyweb import pydom
|
||||||
|
|
||||||
display(sys.version, target="system-info")
|
display(sys.version, target="system-info")
|
||||||
|
|
||||||
@@ -19,15 +19,18 @@ def on_click():
|
|||||||
tstr = "{:02d}/{:02d}/{:04d} {:02d}:{:02d}:{:02d}"
|
tstr = "{:02d}/{:02d}/{:04d} {:02d}:{:02d}:{:02d}"
|
||||||
timenow = tstr.format(tnow[2], tnow[1], tnow[0], *tnow[2:])
|
timenow = tstr.format(tnow[2], tnow[1], tnow[0], *tnow[2:])
|
||||||
|
|
||||||
display(f"Hello from PyScript, time is: {timenow}", append=False, target="#result")
|
display(f"Hello from PyScript, time is: {timenow}", append=False, target="result")
|
||||||
|
|
||||||
|
|
||||||
@when("click", "#color-button")
|
@when("click", "#color-button")
|
||||||
def on_color_click(event):
|
def on_color_click(event):
|
||||||
btn = dom["#result"]
|
btn = pydom["#result"]
|
||||||
btn.style["background-color"] = f"#{random.randrange(0x1000000):06x}"
|
btn.style["background-color"] = f"#{random.randrange(0x1000000):06x}"
|
||||||
|
|
||||||
|
|
||||||
@when("click", "#color-reset-button")
|
@when("click", "#color-reset-button")
|
||||||
def reset_color(*args, **kwargs):
|
def reset_color(*args, **kwargs):
|
||||||
dom["#result"].style["background-color"] = "white"
|
pydom["#result"].style["background-color"] = "white"
|
||||||
|
|
||||||
|
|
||||||
|
# btn_reset = pydom["#color-reset-button"][0].when('click', reset_color)
|
||||||
|
|||||||
@@ -2,13 +2,22 @@ from unittest import mock
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pyscript import document, when
|
from pyscript import document, when
|
||||||
from pyscript.web import dom
|
from pyweb import pydom
|
||||||
from pyscript.web import elements as el
|
|
||||||
|
|
||||||
|
|
||||||
class TestDocument:
|
class TestDocument:
|
||||||
def test__element(self):
|
def test__element(self):
|
||||||
assert dom._js == document
|
assert pydom._js == document
|
||||||
|
|
||||||
|
def test_no_parent(self):
|
||||||
|
assert pydom.parent is None
|
||||||
|
|
||||||
|
def test_create_element(self):
|
||||||
|
new_el = pydom.create("div")
|
||||||
|
assert isinstance(new_el, pydom.BaseElement)
|
||||||
|
assert new_el._js.tagName == "DIV"
|
||||||
|
# EXPECT the new element to be associated with the document
|
||||||
|
assert new_el.parent == None
|
||||||
|
|
||||||
|
|
||||||
def test_getitem_by_id():
|
def test_getitem_by_id():
|
||||||
@@ -17,14 +26,14 @@ def test_getitem_by_id():
|
|||||||
txt = "You found test_id_selector"
|
txt = "You found test_id_selector"
|
||||||
selector = f"#{id_}"
|
selector = f"#{id_}"
|
||||||
# EXPECT the element to be found by id
|
# EXPECT the element to be found by id
|
||||||
result = dom[selector]
|
result = pydom[selector]
|
||||||
div = result[0]
|
div = result[0]
|
||||||
# EXPECT the element text value to match what we expect and what
|
# EXPECT the element text value to match what we expect and what
|
||||||
# the JS document.querySelector API would return
|
# the JS document.querySelector API would return
|
||||||
assert document.querySelector(selector).innerHTML == div.html == txt
|
assert document.querySelector(selector).innerHTML == div.html == txt
|
||||||
# EXPECT the results to be of the right types
|
# EXPECT the results to be of the right types
|
||||||
assert isinstance(div, el.BaseElement)
|
assert isinstance(div, pydom.BaseElement)
|
||||||
assert isinstance(result, dom.ElementCollection)
|
assert isinstance(result, pydom.ElementCollection)
|
||||||
|
|
||||||
|
|
||||||
def test_getitem_by_class():
|
def test_getitem_by_class():
|
||||||
@@ -34,7 +43,7 @@ def test_getitem_by_class():
|
|||||||
"test_selector_w_children_child_1",
|
"test_selector_w_children_child_1",
|
||||||
]
|
]
|
||||||
expected_class = "a-test-class"
|
expected_class = "a-test-class"
|
||||||
result = dom[f".{expected_class}"]
|
result = pydom[f".{expected_class}"]
|
||||||
div = result[0]
|
div = result[0]
|
||||||
|
|
||||||
# EXPECT to find exact number of elements with the class in the page (== 3)
|
# EXPECT to find exact number of elements with the class in the page (== 3)
|
||||||
@@ -45,7 +54,7 @@ def test_getitem_by_class():
|
|||||||
|
|
||||||
|
|
||||||
def test_read_n_write_collection_elements():
|
def test_read_n_write_collection_elements():
|
||||||
elements = dom[".multi-elems"]
|
elements = pydom[".multi-elems"]
|
||||||
|
|
||||||
for element in elements:
|
for element in elements:
|
||||||
assert element.html == f"Content {element.id.replace('#', '')}"
|
assert element.html == f"Content {element.id.replace('#', '')}"
|
||||||
@@ -60,15 +69,15 @@ class TestElement:
|
|||||||
def test_query(self):
|
def test_query(self):
|
||||||
# GIVEN an existing element on the page, with at least 1 child element
|
# GIVEN an existing element on the page, with at least 1 child element
|
||||||
id_ = "test_selector_w_children"
|
id_ = "test_selector_w_children"
|
||||||
parent_div = dom[f"#{id_}"][0]
|
parent_div = pydom[f"#{id_}"][0]
|
||||||
|
|
||||||
# EXPECT it to be able to query for the first child element
|
# EXPECT it to be able to query for the first child element
|
||||||
div = parent_div.find("div")[0]
|
div = parent_div.find("div")[0]
|
||||||
|
|
||||||
# EXPECT the new element to be associated with the parent
|
# EXPECT the new element to be associated with the parent
|
||||||
assert div.parent == parent_div
|
assert div.parent == parent_div
|
||||||
# EXPECT the new element to be a el.BaseElement
|
# EXPECT the new element to be a BaseElement
|
||||||
assert isinstance(div, el.BaseElement)
|
assert isinstance(div, pydom.BaseElement)
|
||||||
# EXPECT the div attributes to be == to how they are configured in the page
|
# EXPECT the div attributes to be == to how they are configured in the page
|
||||||
assert div.html == "Child 1"
|
assert div.html == "Child 1"
|
||||||
assert div.id == "test_selector_w_children_child_1"
|
assert div.id == "test_selector_w_children_child_1"
|
||||||
@@ -77,8 +86,8 @@ class TestElement:
|
|||||||
# GIVEN 2 different Elements pointing to the same underlying element
|
# GIVEN 2 different Elements pointing to the same underlying element
|
||||||
id_ = "test_id_selector"
|
id_ = "test_id_selector"
|
||||||
selector = f"#{id_}"
|
selector = f"#{id_}"
|
||||||
div = dom[selector][0]
|
div = pydom[selector][0]
|
||||||
div2 = dom[selector][0]
|
div2 = pydom[selector][0]
|
||||||
|
|
||||||
# EXPECT them to be equal
|
# EXPECT them to be equal
|
||||||
assert div == div2
|
assert div == div2
|
||||||
@@ -93,27 +102,27 @@ class TestElement:
|
|||||||
|
|
||||||
def test_append_element(self):
|
def test_append_element(self):
|
||||||
id_ = "element-append-tests"
|
id_ = "element-append-tests"
|
||||||
div = dom[f"#{id_}"][0]
|
div = pydom[f"#{id_}"][0]
|
||||||
len_children_before = len(div.children)
|
len_children_before = len(div.children)
|
||||||
new_el = el.p("new element")
|
new_el = div.create("p")
|
||||||
div.append(new_el)
|
div.append(new_el)
|
||||||
assert len(div.children) == len_children_before + 1
|
assert len(div.children) == len_children_before + 1
|
||||||
assert div.children[-1] == new_el
|
assert div.children[-1] == new_el
|
||||||
|
|
||||||
def test_append_js_element(self):
|
def test_append_js_element(self):
|
||||||
id_ = "element-append-tests"
|
id_ = "element-append-tests"
|
||||||
div = dom[f"#{id_}"][0]
|
div = pydom[f"#{id_}"][0]
|
||||||
len_children_before = len(div.children)
|
len_children_before = len(div.children)
|
||||||
new_el = el.p("new element")
|
new_el = div.create("p")
|
||||||
div.append(new_el._js)
|
div.append(new_el._js)
|
||||||
assert len(div.children) == len_children_before + 1
|
assert len(div.children) == len_children_before + 1
|
||||||
assert div.children[-1] == new_el
|
assert div.children[-1] == new_el
|
||||||
|
|
||||||
def test_append_collection(self):
|
def test_append_collection(self):
|
||||||
id_ = "element-append-tests"
|
id_ = "element-append-tests"
|
||||||
div = dom[f"#{id_}"][0]
|
div = pydom[f"#{id_}"][0]
|
||||||
len_children_before = len(div.children)
|
len_children_before = len(div.children)
|
||||||
collection = dom[".collection"]
|
collection = pydom[".collection"]
|
||||||
div.append(collection)
|
div.append(collection)
|
||||||
assert len(div.children) == len_children_before + len(collection)
|
assert len(div.children) == len_children_before + len(collection)
|
||||||
|
|
||||||
@@ -123,16 +132,16 @@ class TestElement:
|
|||||||
def test_read_classes(self):
|
def test_read_classes(self):
|
||||||
id_ = "test_class_selector"
|
id_ = "test_class_selector"
|
||||||
expected_class = "a-test-class"
|
expected_class = "a-test-class"
|
||||||
div = dom[f"#{id_}"][0]
|
div = pydom[f"#{id_}"][0]
|
||||||
assert div.classes == [expected_class]
|
assert div.classes == [expected_class]
|
||||||
|
|
||||||
def test_add_remove_class(self):
|
def test_add_remove_class(self):
|
||||||
id_ = "div-no-classes"
|
id_ = "div-no-classes"
|
||||||
classname = "tester-class"
|
classname = "tester-class"
|
||||||
div = dom[f"#{id_}"][0]
|
div = pydom[f"#{id_}"][0]
|
||||||
assert not div.classes
|
assert not div.classes
|
||||||
div.add_class(classname)
|
div.add_class(classname)
|
||||||
same_div = dom[f"#{id_}"][0]
|
same_div = pydom[f"#{id_}"][0]
|
||||||
assert div.classes == [classname] == same_div.classes
|
assert div.classes == [classname] == same_div.classes
|
||||||
div.remove_class(classname)
|
div.remove_class(classname)
|
||||||
assert div.classes == [] == same_div.classes
|
assert div.classes == [] == same_div.classes
|
||||||
@@ -140,7 +149,7 @@ class TestElement:
|
|||||||
def test_when_decorator(self):
|
def test_when_decorator(self):
|
||||||
called = False
|
called = False
|
||||||
|
|
||||||
just_a_button = dom["#a-test-button"][0]
|
just_a_button = pydom["#a-test-button"][0]
|
||||||
|
|
||||||
@when("click", just_a_button)
|
@when("click", just_a_button)
|
||||||
def on_click(event):
|
def on_click(event):
|
||||||
@@ -148,7 +157,7 @@ class TestElement:
|
|||||||
called = True
|
called = True
|
||||||
|
|
||||||
# Now let's simulate a click on the button (using the low level JS API)
|
# Now let's simulate a click on the button (using the low level JS API)
|
||||||
# so we don't risk dom getting in the way
|
# so we don't risk pydom getting in the way
|
||||||
assert not called
|
assert not called
|
||||||
just_a_button._js.click()
|
just_a_button._js.click()
|
||||||
|
|
||||||
@@ -156,7 +165,7 @@ class TestElement:
|
|||||||
|
|
||||||
def test_html_attribute(self):
|
def test_html_attribute(self):
|
||||||
# GIVEN an existing element on the page with a known empty text content
|
# GIVEN an existing element on the page with a known empty text content
|
||||||
div = dom["#element_attribute_tests"][0]
|
div = pydom["#element_attribute_tests"][0]
|
||||||
|
|
||||||
# WHEN we set the html attribute
|
# WHEN we set the html attribute
|
||||||
div.html = "<b>New Content</b>"
|
div.html = "<b>New Content</b>"
|
||||||
@@ -168,7 +177,7 @@ class TestElement:
|
|||||||
|
|
||||||
def test_text_attribute(self):
|
def test_text_attribute(self):
|
||||||
# GIVEN an existing element on the page with a known empty text content
|
# GIVEN an existing element on the page with a known empty text content
|
||||||
div = dom["#element_attribute_tests"][0]
|
div = pydom["#element_attribute_tests"][0]
|
||||||
|
|
||||||
# WHEN we set the html attribute
|
# WHEN we set the html attribute
|
||||||
div.text = "<b>New Content</b>"
|
div.text = "<b>New Content</b>"
|
||||||
@@ -181,12 +190,12 @@ class TestElement:
|
|||||||
|
|
||||||
class TestCollection:
|
class TestCollection:
|
||||||
def test_iter_eq_children(self):
|
def test_iter_eq_children(self):
|
||||||
elements = dom[".multi-elems"]
|
elements = pydom[".multi-elems"]
|
||||||
assert [el for el in elements] == [el for el in elements.children]
|
assert [el for el in elements] == [el for el in elements.children]
|
||||||
assert len(elements) == 3
|
assert len(elements) == 3
|
||||||
|
|
||||||
def test_slices(self):
|
def test_slices(self):
|
||||||
elements = dom[".multi-elems"]
|
elements = pydom[".multi-elems"]
|
||||||
assert elements[0]
|
assert elements[0]
|
||||||
_slice = elements[:2]
|
_slice = elements[:2]
|
||||||
assert len(_slice) == 2
|
assert len(_slice) == 2
|
||||||
@@ -196,26 +205,26 @@ class TestCollection:
|
|||||||
|
|
||||||
def test_style_rule(self):
|
def test_style_rule(self):
|
||||||
selector = ".multi-elems"
|
selector = ".multi-elems"
|
||||||
elements = dom[selector]
|
elements = pydom[selector]
|
||||||
for el in elements:
|
for el in elements:
|
||||||
assert el.style["background-color"] != "red"
|
assert el.style["background-color"] != "red"
|
||||||
|
|
||||||
elements.style["background-color"] = "red"
|
elements.style["background-color"] = "red"
|
||||||
|
|
||||||
for i, el in enumerate(dom[selector]):
|
for i, el in enumerate(pydom[selector]):
|
||||||
assert elements[i].style["background-color"] == "red"
|
assert elements[i].style["background-color"] == "red"
|
||||||
assert el.style["background-color"] == "red"
|
assert el.style["background-color"] == "red"
|
||||||
|
|
||||||
elements.style.remove("background-color")
|
elements.style.remove("background-color")
|
||||||
|
|
||||||
for i, el in enumerate(dom[selector]):
|
for i, el in enumerate(pydom[selector]):
|
||||||
assert el.style["background-color"] != "red"
|
assert el.style["background-color"] != "red"
|
||||||
assert elements[i].style["background-color"] != "red"
|
assert elements[i].style["background-color"] != "red"
|
||||||
|
|
||||||
def test_when_decorator(self):
|
def test_when_decorator(self):
|
||||||
called = False
|
called = False
|
||||||
|
|
||||||
buttons_collection = dom["button"]
|
buttons_collection = pydom["button"]
|
||||||
|
|
||||||
@when("click", buttons_collection)
|
@when("click", buttons_collection)
|
||||||
def on_click(event):
|
def on_click(event):
|
||||||
@@ -223,7 +232,7 @@ class TestCollection:
|
|||||||
called = True
|
called = True
|
||||||
|
|
||||||
# Now let's simulate a click on the button (using the low level JS API)
|
# Now let's simulate a click on the button (using the low level JS API)
|
||||||
# so we don't risk dom getting in the way
|
# so we don't risk pydom getting in the way
|
||||||
assert not called
|
assert not called
|
||||||
for button in buttons_collection:
|
for button in buttons_collection:
|
||||||
button._js.click()
|
button._js.click()
|
||||||
@@ -233,32 +242,32 @@ class TestCollection:
|
|||||||
|
|
||||||
class TestCreation:
|
class TestCreation:
|
||||||
def test_create_document_element(self):
|
def test_create_document_element(self):
|
||||||
# TODO: This test should probably be removed since it's testing the elements module
|
new_el = pydom.create("div")
|
||||||
new_el = el.div("new element")
|
|
||||||
new_el.id = "new_el_id"
|
new_el.id = "new_el_id"
|
||||||
assert isinstance(new_el, el.BaseElement)
|
assert isinstance(new_el, pydom.BaseElement)
|
||||||
assert new_el._js.tagName == "DIV"
|
assert new_el._js.tagName == "DIV"
|
||||||
# EXPECT the new element to be associated with the document
|
# EXPECT the new element to be associated with the document
|
||||||
assert new_el.parent == None
|
assert new_el.parent == None
|
||||||
dom.body.append(new_el)
|
pydom.body.append(new_el)
|
||||||
|
|
||||||
assert dom["#new_el_id"][0].parent == dom.body
|
assert pydom["#new_el_id"][0].parent == pydom.body
|
||||||
|
|
||||||
def test_create_element_child(self):
|
def test_create_element_child(self):
|
||||||
selector = "#element-creation-test"
|
selector = "#element-creation-test"
|
||||||
parent_div = dom[selector][0]
|
parent_div = pydom[selector][0]
|
||||||
|
|
||||||
# Creating an element from another element automatically creates that element
|
# Creating an element from another element automatically creates that element
|
||||||
# as a child of the original element
|
# as a child of the original element
|
||||||
new_el = el.p("a div", classes=["code-description"], html="Ciao PyScripters!")
|
new_el = parent_div.create(
|
||||||
parent_div.append(new_el)
|
"p", classes=["code-description"], html="Ciao PyScripters!"
|
||||||
|
)
|
||||||
|
|
||||||
assert isinstance(new_el, el.BaseElement)
|
assert isinstance(new_el, pydom.BaseElement)
|
||||||
assert new_el._js.tagName == "P"
|
assert new_el._js.tagName == "P"
|
||||||
|
|
||||||
# EXPECT the new element to be associated with the document
|
# EXPECT the new element to be associated with the document
|
||||||
assert new_el.parent == parent_div
|
assert new_el.parent == parent_div
|
||||||
assert dom[selector][0].children[0] == new_el
|
|
||||||
|
assert pydom[selector][0].children[0] == new_el
|
||||||
|
|
||||||
|
|
||||||
class TestInput:
|
class TestInput:
|
||||||
@@ -272,7 +281,7 @@ class TestInput:
|
|||||||
def test_value(self):
|
def test_value(self):
|
||||||
for id_ in self.input_ids:
|
for id_ in self.input_ids:
|
||||||
expected_type = id_.split("_")[-1]
|
expected_type = id_.split("_")[-1]
|
||||||
result = dom[f"#{id_}"]
|
result = pydom[f"#{id_}"]
|
||||||
input_el = result[0]
|
input_el = result[0]
|
||||||
assert input_el._js.type == expected_type
|
assert input_el._js.type == expected_type
|
||||||
assert input_el.value == f"Content {id_}" == input_el._js.value
|
assert input_el.value == f"Content {id_}" == input_el._js.value
|
||||||
@@ -290,7 +299,7 @@ class TestInput:
|
|||||||
|
|
||||||
def test_set_value_collection(self):
|
def test_set_value_collection(self):
|
||||||
for id_ in self.input_ids:
|
for id_ in self.input_ids:
|
||||||
input_el = dom[f"#{id_}"]
|
input_el = pydom[f"#{id_}"]
|
||||||
|
|
||||||
assert input_el.value[0] == f"Content {id_}" == input_el[0].value
|
assert input_el.value[0] == f"Content {id_}" == input_el[0].value
|
||||||
|
|
||||||
@@ -299,35 +308,35 @@ class TestInput:
|
|||||||
assert input_el.value[0] == new_value == input_el[0].value
|
assert input_el.value[0] == new_value == input_el[0].value
|
||||||
|
|
||||||
def test_element_without_value(self):
|
def test_element_without_value(self):
|
||||||
result = dom[f"#tests-terminal"][0]
|
result = pydom[f"#tests-terminal"][0]
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
result.value = "some value"
|
result.value = "some value"
|
||||||
|
|
||||||
def test_element_without_collection(self):
|
def test_element_without_collection(self):
|
||||||
result = dom[f"#tests-terminal"]
|
result = pydom[f"#tests-terminal"]
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
result.value = "some value"
|
result.value = "some value"
|
||||||
|
|
||||||
def test_element_without_collection(self):
|
def test_element_without_collection(self):
|
||||||
result = dom[f"#tests-terminal"]
|
result = pydom[f"#tests-terminal"]
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
result.value = "some value"
|
result.value = "some value"
|
||||||
|
|
||||||
|
|
||||||
class TestSelect:
|
class TestSelect:
|
||||||
def test_select_options_iter(self):
|
def test_select_options_iter(self):
|
||||||
select = dom[f"#test_select_element_w_options"][0]
|
select = pydom[f"#test_select_element_w_options"][0]
|
||||||
|
|
||||||
for i, option in enumerate(select.options, 1):
|
for i, option in enumerate(select.options, 1):
|
||||||
assert option.value == f"{i}"
|
assert option.value == f"{i}"
|
||||||
assert option.html == f"Option {i}"
|
assert option.html == f"Option {i}"
|
||||||
|
|
||||||
def test_select_options_len(self):
|
def test_select_options_len(self):
|
||||||
select = dom[f"#test_select_element_w_options"][0]
|
select = pydom[f"#test_select_element_w_options"][0]
|
||||||
assert len(select.options) == 2
|
assert len(select.options) == 2
|
||||||
|
|
||||||
def test_select_options_clear(self):
|
def test_select_options_clear(self):
|
||||||
select = dom[f"#test_select_element_to_clear"][0]
|
select = pydom[f"#test_select_element_to_clear"][0]
|
||||||
assert len(select.options) == 3
|
assert len(select.options) == 3
|
||||||
|
|
||||||
select.options.clear()
|
select.options.clear()
|
||||||
@@ -336,7 +345,7 @@ class TestSelect:
|
|||||||
|
|
||||||
def test_select_element_add(self):
|
def test_select_element_add(self):
|
||||||
# GIVEN the existing select element with no options
|
# GIVEN the existing select element with no options
|
||||||
select = dom[f"#test_select_element"][0]
|
select = pydom[f"#test_select_element"][0]
|
||||||
|
|
||||||
# EXPECT the select element to have no options
|
# EXPECT the select element to have no options
|
||||||
assert len(select.options) == 0
|
assert len(select.options) == 0
|
||||||
@@ -417,7 +426,7 @@ class TestSelect:
|
|||||||
|
|
||||||
def test_select_options_remove(self):
|
def test_select_options_remove(self):
|
||||||
# GIVEN the existing select element with 3 options
|
# GIVEN the existing select element with 3 options
|
||||||
select = dom[f"#test_select_element_to_remove"][0]
|
select = pydom[f"#test_select_element_to_remove"][0]
|
||||||
|
|
||||||
# EXPECT the select element to have 3 options
|
# EXPECT the select element to have 3 options
|
||||||
assert len(select.options) == 4
|
assert len(select.options) == 4
|
||||||
@@ -439,7 +448,7 @@ class TestSelect:
|
|||||||
|
|
||||||
def test_select_get_selected_option(self):
|
def test_select_get_selected_option(self):
|
||||||
# GIVEN the existing select element with one selected option
|
# GIVEN the existing select element with one selected option
|
||||||
select = dom[f"#test_select_element_w_options"][0]
|
select = pydom[f"#test_select_element_w_options"][0]
|
||||||
|
|
||||||
# WHEN we get the selected option
|
# WHEN we get the selected option
|
||||||
selected_option = select.options.selected
|
selected_option = select.options.selected
|
||||||
|
|||||||
@@ -101,11 +101,10 @@ class TestElements(PyScriptTest):
|
|||||||
code_ = f"""
|
code_ = f"""
|
||||||
from pyscript import when
|
from pyscript import when
|
||||||
<script type="{interpreter}">
|
<script type="{interpreter}">
|
||||||
from pyscript.web import dom
|
from pyweb import pydom
|
||||||
from pyscript.web.elements import {el_type}
|
from pyweb.ui.elements import {el_type}
|
||||||
|
|
||||||
el = {el_type}({attributes})
|
el = {el_type}({attributes})
|
||||||
dom.body.append(el)
|
pydom.body.append(el)
|
||||||
</script>
|
</script>
|
||||||
"""
|
"""
|
||||||
self.pyscript_run(code_)
|
self.pyscript_run(code_)
|
||||||
|
|||||||
9
pyscript.core/types/stdlib/pyscript.d.ts
vendored
9
pyscript.core/types/stdlib/pyscript.d.ts
vendored
@@ -17,5 +17,14 @@ declare namespace _default {
|
|||||||
};
|
};
|
||||||
"websocket.py": string;
|
"websocket.py": string;
|
||||||
};
|
};
|
||||||
|
let pyweb: {
|
||||||
|
"__init__.py": string;
|
||||||
|
"media.py": string;
|
||||||
|
"pydom.py": string;
|
||||||
|
ui: {
|
||||||
|
"__init__.py": string;
|
||||||
|
"elements.py": string;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export default _default;
|
export default _default;
|
||||||
|
|||||||
Reference in New Issue
Block a user