mirror of
https://github.com/pyscript/pyscript.git
synced 2026-04-04 20:00:24 -04:00
UI creation API (#1960)
* add JSProperty to pydom so it can be used to create descriptors mapping to specific JS attributes * add pyweb.ui * fix pyweb imports * fix link and a elements and add a script element * fix imports and add initialization to load resources to shoelace module * new pyweb.ui test folder * remove comments * add Icon to shoelace components * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * use html property rather than accessing _js directly * add markdown suppport * move examples section out of stdlib pyweb to examples.py in the demo itself * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * simplify demo code * improve docstrings * precommit fixes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * simplify code for main page * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add load_resources to markdown * add showlace extra style dynamically * precommit * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add gallery files * add global attributes and dynamic property assignment * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add shoelace radio component (#1961) * add shoelace radio component * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * fix type that lead using the the JSDescriptor directly instead of the factory method * add missing marked dependency * refactor gallary to simplify codebase * precommig lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add text attribute to pydom Elements * add global JS attributes to elements and improve demos * lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix image instantiation on card since the API has changed * add attributes to all classes in elements * lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * change example creation functions to take the label and the object directly * fix input name clashing with input keyword * refactor examples to better simplify and automate * fix div clashing names * fix demo left menu width * simplify base elements demo by moving all the examples to the examples module * rename Grid to grid to align to other elements * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * reorg classes order in elements.py and add missing elements to examples * fix issue related to now importing div from pyweb.ui * improve demo * link and fix spelling typo * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add a bunch more elements (#1966) * Add copy button * Add skeleton and spinner * Add Switch * Add text area * Add more elements * More styling to each component * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Fabio Pliger <fpliger@users.noreply.github.com> * Add radio group (#1963) * add radio group * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Fabio Pliger <fpliger@users.noreply.github.com> * Small tweaks to main demo page (#1962) * Small tweaks to main demo page * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Fabio Pliger <fpliger@users.noreply.github.com> * fix post merge issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed issues with renaming Grid to grid, after we merged * lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add multple HTML elements in alphabetical order from abbr to em * fix attributes of some of the elements added in the previous commit and add embed * fix embed attributes and add fieldset * add figcation, figure and form. Also fix ordering of definitoin of img and input_ * add style and lint * wrap up adding all major html elements * fix test failing due to different error message from fake server compared to a real test server * change default docstring associated with all classes dynamically patched * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add pyweb tests * lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add global attributes and change abbr test * fix abbr to inherit from TextElementBase * add address test and improve error messaging when ElementBase gets a bad input as style * change test helper function to be more flexible on attributes and manage content vs non content based elements. Also adds area tests * add test for more elements up to caption * fix canvas and caption as elements that have content and fix name typo on figcaption * fix another typo on figcaption * minor fixes and complete tests for all elements * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * adapt shoelace to latest upates in ui.elements * fix issue with example code not showing created button * move global attributes to base class * replace all the calls to _add_js_properties with defining attributes directly in the classes * finish moving all properties manually on each class * remove _add_js_properties * replace JSProperty with js_property * replace JSProperty with js_property * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed merge conflicts * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove js_property and just use JSProperty with name and allow_nones as arguments * fix bug around Element not being able to map global attributes in subclasses * remove js_property and fix references * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * precommit * precommit again * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * enable pyweb on micropython * switch examples to micropython * fix pydom issue with micropython, created by the monkeypatch around JsProxy * print micropython version on micropython pydom example * lint and remove of textwrap in stdlib for micropython compatibility * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added msising attributes on the option Element. Tests are all passing now * fix tests * lint * temp ugly fix for micropython, using the when decorator with a function without arguments * small fixes and improvements to examples * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix examples and broken link from the removal of markdown and shoelace from stdlib * lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * dynamically change the server address in tests * use the right element in test_a * lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * skipping test_audio in CI due to different behavior of fake_server vs a real server, that runs in local tests * add conditional expected_missing_file_errors property to manage different behaviour between local tests and CI (due to fake_server) * lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Askat <aaskat@users.noreply.github.com> Co-authored-by: Fábio Rosado <hello@fabiorosado.dev> Co-authored-by: Andrea Giammarchi <andrea.giammarchi@gmail.com>
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
<script type="module" src="../dist/core.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="py" src="pydom.py"></script>
|
||||
<script type="mpy" src="pydom.py"></script>
|
||||
|
||||
<button id="just-a-button">Click For Time</button>
|
||||
<button id="color-button">Click For Color</button>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import random
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime as dt
|
||||
|
||||
from pyscript import display, when
|
||||
from pyweb import pydom
|
||||
|
||||
display(sys.version, target="system-info")
|
||||
|
||||
|
||||
@when("click", "#just-a-button")
|
||||
def on_click():
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
<body>
|
||||
<script type="mpy" src="pydom.py"></script>
|
||||
|
||||
<div id="system-info"></div>
|
||||
|
||||
<button id="just-a-button">Click For Time</button>
|
||||
<button id="color-button">Click For Color</button>
|
||||
<button id="color-reset-button">Reset Color</button>
|
||||
|
||||
251
pyscript.core/test/ui/demo.py
Normal file
251
pyscript.core/test/ui/demo.py
Normal file
@@ -0,0 +1,251 @@
|
||||
try:
|
||||
from textwrap import dedent
|
||||
except ImportError:
|
||||
dedent = lambda x: x
|
||||
|
||||
import examples
|
||||
import shoelace
|
||||
import styles
|
||||
from markdown import markdown
|
||||
from pyscript import when, window
|
||||
from pyweb import pydom
|
||||
from pyweb.ui import elements as el
|
||||
from pyweb.ui.elements import a, button, div, grid, h1, h2, h3
|
||||
|
||||
MAIN_PAGE_MARKDOWN = dedent(
|
||||
"""
|
||||
## What is pyweb.ui?
|
||||
Pyweb UI is a totally immagnary exercise atm but..... imagine it is a Python library that allows you to create
|
||||
web applications using Python only.
|
||||
|
||||
It is based on base HTML/JS components but is extensible, for instance, it can have a [Shoelace](https://shoelace.style/) backend...
|
||||
|
||||
PyWeb is a Python library that allows you to create web applications using Python only.
|
||||
|
||||
## What can I do with Pyweb.ui?
|
||||
|
||||
You can create web applications using Python only.
|
||||
"""
|
||||
)
|
||||
|
||||
# First thing we do is to load all the external resources we need
|
||||
shoelace.load_resources()
|
||||
|
||||
|
||||
# Let's define some convenience functions first
|
||||
def create_component_details(component_label, component):
|
||||
"""Create a component details card.
|
||||
|
||||
Args:
|
||||
component (str): The name of the component to create.
|
||||
|
||||
Returns:
|
||||
the component created
|
||||
|
||||
"""
|
||||
# Get the example from the examples catalog
|
||||
example = component["instance"]
|
||||
details = (
|
||||
getattr(example, "__doc__", "")
|
||||
or f"Details missing for component {component_label}"
|
||||
)
|
||||
|
||||
return div(
|
||||
[
|
||||
# Title and description (description is picked from the class docstring)
|
||||
h1(component_label),
|
||||
markdown(details),
|
||||
# Example section
|
||||
h2("Example:"),
|
||||
create_component_example(component["instance"], component["code"]),
|
||||
],
|
||||
style={"margin": "20px"},
|
||||
)
|
||||
|
||||
|
||||
def add_component_section(component_label, component, parent_div):
|
||||
"""Create a link to a component and add it to the left panel.
|
||||
|
||||
Args:
|
||||
component (str): The name of the component to add.
|
||||
|
||||
Returns:
|
||||
the component created
|
||||
|
||||
"""
|
||||
# Create the component link element
|
||||
div_ = div(
|
||||
a(component_label, href="#"),
|
||||
style={"display": "block", "text-align": "center", "margin": "auto"},
|
||||
)
|
||||
|
||||
# Create a handler that opens the component details when the link is clicked
|
||||
@when("click", div_)
|
||||
def _change():
|
||||
new_main = create_component_details(component_label, component)
|
||||
main_area.html = ""
|
||||
main_area.append(new_main)
|
||||
|
||||
# Add the new link element to the parent div (left panel)
|
||||
parent_div.append(div_)
|
||||
return div_
|
||||
|
||||
|
||||
def create_component_example(widget, code):
|
||||
"""Create a grid div with the widget on the left side and the relate code
|
||||
on the right side.
|
||||
|
||||
Args:
|
||||
widget (ElementBase): The widget to add to the grid.
|
||||
code (str): The code to add to the grid.
|
||||
|
||||
Returns:
|
||||
the grid created
|
||||
|
||||
"""
|
||||
# Create the grid that splits the window in two columns (25% and 75%)
|
||||
grid_ = grid("29% 2% 74%")
|
||||
|
||||
# Add the widget
|
||||
grid_.append(div(widget, style=styles.STYLE_EXAMPLE_INSTANCE))
|
||||
|
||||
# Add the code div
|
||||
widget_code = markdown(dedent(f"""```python\n{code}\n```"""))
|
||||
grid_.append(shoelace.Divider(vertical=True))
|
||||
grid_.append(div(widget_code, style=styles.STYLE_CODE_BLOCK))
|
||||
|
||||
return grid_
|
||||
|
||||
|
||||
def create_main_area():
|
||||
"""Create the main area of the right side of page, with the description of the
|
||||
demo itself and how to use it.
|
||||
|
||||
Returns:
|
||||
the main area
|
||||
|
||||
"""
|
||||
div_ = div(
|
||||
[
|
||||
h1("Welcome to PyWeb UI!", style={"text-align": "center"}),
|
||||
markdown(MAIN_PAGE_MARKDOWN),
|
||||
]
|
||||
)
|
||||
|
||||
main = el.main(
|
||||
style={
|
||||
"padding-top": "4rem",
|
||||
"padding-bottom": "7rem",
|
||||
"max-width": "52rem",
|
||||
"margin-left": "auto",
|
||||
"margin-right": "auto",
|
||||
"padding-left": "1.5rem",
|
||||
"padding-right": "1.5rem",
|
||||
"width": "100%",
|
||||
}
|
||||
)
|
||||
main.append(div_)
|
||||
|
||||
return main
|
||||
|
||||
|
||||
def create_basic_components_page(label, kit_name):
|
||||
"""Create the basic components page.
|
||||
|
||||
Returns:
|
||||
the main area
|
||||
|
||||
"""
|
||||
div_ = div(h2(label))
|
||||
|
||||
for component_label, component in examples.kits[kit_name].items():
|
||||
div_.append(h3(component_label))
|
||||
div_.append(create_component_example(component["instance"], component["code"]))
|
||||
|
||||
return div_
|
||||
|
||||
|
||||
# ********** CREATE ALL THE LAYOUT **********
|
||||
|
||||
main_grid = grid("140px 20px auto", style={"min-height": "100%"})
|
||||
|
||||
# ********** MAIN PANEL **********
|
||||
main_area = create_main_area()
|
||||
|
||||
|
||||
def write_to_main(content):
|
||||
main_area.html = ""
|
||||
main_area.append(content)
|
||||
|
||||
|
||||
def restore_home():
|
||||
write_to_main(create_main_area())
|
||||
|
||||
|
||||
def basic_components():
|
||||
write_to_main(
|
||||
create_basic_components_page(label="Basic Components", kit_name="elements")
|
||||
)
|
||||
# Make sure we highlight the code
|
||||
window.hljs.highlightAll()
|
||||
|
||||
|
||||
def markdown_components():
|
||||
write_to_main(create_basic_components_page(label="", kit_name="markdown"))
|
||||
|
||||
|
||||
def create_new_section(title, parent_div):
|
||||
basic_components_text = h3(
|
||||
title, style={"text-align": "left", "margin": "20px auto 0"}
|
||||
)
|
||||
parent_div.append(basic_components_text)
|
||||
parent_div.append(
|
||||
shoelace.Divider(style={"margin-top": "5px", "margin-bottom": "30px"})
|
||||
)
|
||||
return basic_components_text
|
||||
|
||||
|
||||
# ********** LEFT PANEL **********
|
||||
left_div = div()
|
||||
left_panel_title = h1(
|
||||
"PyWeb.UI", style={"text-align": "center", "margin": "20px auto 30px"}
|
||||
)
|
||||
left_div.append(left_panel_title)
|
||||
left_div.append(shoelace.Divider(style={"margin-bottom": "30px"}))
|
||||
# Let's map the creation of the main area to when the user clocks on "Components"
|
||||
when("click", left_panel_title)(restore_home)
|
||||
|
||||
# BASIC COMPONENTS
|
||||
basic_components_text = h3(
|
||||
"Basic Components",
|
||||
style={"text-align": "left", "margin": "20px auto 0", "cursor": "pointer"},
|
||||
)
|
||||
left_div.append(basic_components_text)
|
||||
left_div.append(shoelace.Divider(style={"margin-top": "5px", "margin-bottom": "30px"}))
|
||||
# Let's map the creation of the main area to when the user clocks on "Components"
|
||||
when("click", basic_components_text)(basic_components)
|
||||
|
||||
# MARKDOWN COMPONENTS
|
||||
markdown_title = create_new_section("Markdown", left_div)
|
||||
when("click", markdown_title)(markdown_components)
|
||||
|
||||
|
||||
# SHOELACE COMPONENTS
|
||||
shoe_components_text = h3(
|
||||
"Shoe Components", style={"text-align": "left", "margin": "20px auto 0"}
|
||||
)
|
||||
left_div.append(shoe_components_text)
|
||||
left_div.append(shoelace.Divider(style={"margin-top": "5px", "margin-bottom": "30px"}))
|
||||
|
||||
# Create the links to the components on th left panel
|
||||
print("SHOELACE EXAMPLES", examples.kits["shoelace"])
|
||||
for component_label, component in examples.kits["shoelace"].items():
|
||||
add_component_section(component_label, component, left_div)
|
||||
|
||||
left_div.append(shoelace.Divider(style={"margin-top": "5px", "margin-bottom": "30px"}))
|
||||
left_div.append(a("Gallery", href="gallery.html", style={"text-align": "left"}))
|
||||
# ********** ADD LEFT AND MAIN PANEL TO MAIN **********
|
||||
main_grid.append(left_div)
|
||||
main_grid.append(shoelace.Divider(vertical=True))
|
||||
main_grid.append(main_area)
|
||||
pydom.body.append(main_grid)
|
||||
300
pyscript.core/test/ui/examples.py
Normal file
300
pyscript.core/test/ui/examples.py
Normal file
@@ -0,0 +1,300 @@
|
||||
from markdown import markdown
|
||||
from pyscript import when, window
|
||||
from pyweb import pydom
|
||||
from pyweb.ui.elements import (
|
||||
a,
|
||||
br,
|
||||
button,
|
||||
code,
|
||||
div,
|
||||
grid,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
img,
|
||||
input_,
|
||||
p,
|
||||
small,
|
||||
strong,
|
||||
)
|
||||
from shoelace import (
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
CopyButton,
|
||||
Details,
|
||||
Dialog,
|
||||
Divider,
|
||||
Icon,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Range,
|
||||
Rating,
|
||||
RelativeTime,
|
||||
Skeleton,
|
||||
Spinner,
|
||||
Switch,
|
||||
Tag,
|
||||
Textarea,
|
||||
)
|
||||
|
||||
LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||
details_code = """
|
||||
LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||
Details(LOREM_IPSUM, summary="Try me")
|
||||
"""
|
||||
example_dialog_close_btn = Button("Close")
|
||||
example_dialog = Dialog(div([p(LOREM_IPSUM), example_dialog_close_btn]), label="Try me")
|
||||
example_dialog_btn = Button("Open Dialog")
|
||||
|
||||
|
||||
def toggle_dialog():
|
||||
example_dialog.open = not (example_dialog.open)
|
||||
|
||||
|
||||
when("click", example_dialog_btn)(toggle_dialog)
|
||||
when("click", example_dialog_close_btn)(toggle_dialog)
|
||||
|
||||
pydom.body.append(example_dialog)
|
||||
|
||||
|
||||
# ELEMENTS
|
||||
|
||||
# Button
|
||||
btn = button("Click me!")
|
||||
when("click", btn)(lambda: window.alert("Clicked!"))
|
||||
|
||||
# Inputs
|
||||
inputs_div = div()
|
||||
inputs_code = []
|
||||
for input_type in [
|
||||
"text",
|
||||
"password",
|
||||
"email",
|
||||
"number",
|
||||
"date",
|
||||
"time",
|
||||
"color",
|
||||
"range",
|
||||
]:
|
||||
inputs_div.append(input_(type=input_type, style={"display": "block"}))
|
||||
inputs_code.append(f"input_(type='{input_type}')")
|
||||
|
||||
|
||||
headers_div = div()
|
||||
headers_code = []
|
||||
for header in [h1, h2, h3, h4, h5, h6]:
|
||||
headers_div.append(header(f"{header.tag.upper()} header"))
|
||||
headers_code.append(f'{header.tag}("{header.tag.upper()} header")')
|
||||
headers_code = "\n".join(headers_code)
|
||||
|
||||
rich_input = input_(
|
||||
type="text",
|
||||
name="some name",
|
||||
autofocus=True,
|
||||
pattern="\w{3,16}",
|
||||
placeholder="add text with > 3 chars",
|
||||
required=True,
|
||||
size="20",
|
||||
)
|
||||
inputs_div.append(rich_input)
|
||||
inputs_code.append("# You can create inputs with more options like")
|
||||
inputs_code.append("# this by passing properties as kwargs")
|
||||
inputs_code.append(
|
||||
"input_(type='text', name='some name', autofocus=True, pattern='\\w{3,16}', placeholder='add text with > 3 chars', required=True, size='20')"
|
||||
)
|
||||
inputs_code = "\n".join(inputs_code)
|
||||
|
||||
MARKDOWN_EXAMPLE = """# This is a header
|
||||
|
||||
This is a ~~paragraph~~ text with **bold** and *italic* text in it!
|
||||
"""
|
||||
|
||||
kits = {
|
||||
"shoelace": {
|
||||
"Alert": {
|
||||
"instance": Alert(
|
||||
"This is a standard alert. You can customize its content and even the icon."
|
||||
),
|
||||
"code": "Alert('This is a standard alert. You can customize its content and even the icon.'",
|
||||
},
|
||||
"Icon": {
|
||||
"instance": Icon(name="heart"),
|
||||
"code": 'Icon(name="heart")',
|
||||
},
|
||||
"Button": {
|
||||
"instance": Button("Try me"),
|
||||
"code": 'Button("Try me")',
|
||||
},
|
||||
"Card": {
|
||||
"instance": Card(
|
||||
p("This is a cool card!"),
|
||||
image="https://pyscript.net/assets/images/pyscript-sticker-black.svg",
|
||||
footer=div([Button("More Info"), Rating()]),
|
||||
),
|
||||
"code": """
|
||||
Card(p("This is a cool card!"), image="https://pyscript.net/assets/images/pyscript-sticker-black.svg", footer=div([Button("More Info"), Rating()]))
|
||||
""",
|
||||
},
|
||||
"Details": {
|
||||
"instance": Details(LOREM_IPSUM, summary="Try me"),
|
||||
"code": 'Details(LOREM_IPSUM, summary="Try me")',
|
||||
},
|
||||
"Dialog": {
|
||||
"instance": example_dialog_btn,
|
||||
"code": 'Dialog(div([p(LOREM_IPSUM), Button("Close")]), summary="Try me")',
|
||||
},
|
||||
"Divider": {
|
||||
"instance": Divider(),
|
||||
"code": "Divider()",
|
||||
},
|
||||
"Rating": {
|
||||
"instance": Rating(),
|
||||
"code": "Rating()",
|
||||
},
|
||||
"Radio": {
|
||||
"instance": Radio("Option 42"),
|
||||
"code": code('Radio("Option 42")'),
|
||||
},
|
||||
"Radio Group": {
|
||||
"instance": RadioGroup(
|
||||
[
|
||||
Radio("radio 1", name="radio 1", value=1, style={"margin": "20px"}),
|
||||
Radio("radio 2", name="radio 2", value=2, style={"margin": "20px"}),
|
||||
Radio("radio 3", name="radio 3", value=3, style={"margin": "20px"}),
|
||||
],
|
||||
label="Select an option",
|
||||
),
|
||||
"code": code(
|
||||
"""
|
||||
RadioGroup([Radio("radio 1", name="radio 1", value=1, style={"margin": "20px"}),
|
||||
Radio("radio 2", name="radio 2", value=2, style={"margin": "20px"}),
|
||||
Radio("radio 3", name="radio 3", value=3, style={"margin": "20px"})],
|
||||
label="Select an option"),"""
|
||||
),
|
||||
},
|
||||
"CopyButton": {
|
||||
"instance": CopyButton(
|
||||
value="PyShoes!",
|
||||
copy_label="Copy me!",
|
||||
sucess_label="Copied, check your clipboard!",
|
||||
error_label="Oops, something went wrong!",
|
||||
feedback_timeout=2000,
|
||||
tooltip_placement="top",
|
||||
),
|
||||
"code": 'CopyButton(value="PyShoes!", copy_label="Copy me!", sucess_label="Copied, check your clipboard!", error_label="Oops, something went wrong!", feedback_timeout=2000, tooltip_placement="top")',
|
||||
},
|
||||
"Skeleton": {
|
||||
"instance": Skeleton(effect="pulse"),
|
||||
"code": "Skeleton(effect='pulse')",
|
||||
},
|
||||
"Spinner": {
|
||||
"instance": Spinner(),
|
||||
"code": "Spinner()",
|
||||
},
|
||||
"Switch": {
|
||||
"instance": Switch(name="switch", size="large"),
|
||||
"code": 'Switch(name="switch", size="large")',
|
||||
},
|
||||
"Textarea": {
|
||||
"instance": Textarea(
|
||||
name="textarea",
|
||||
label="Textarea",
|
||||
size="medium",
|
||||
help_text="This is a textarea",
|
||||
resize="auto",
|
||||
),
|
||||
"code": 'Textarea(name="textarea", label="Textarea", size="medium", help_text="This is a textarea", resize="auto")',
|
||||
},
|
||||
"Tag": {
|
||||
"instance": Tag("Tag", variant="primary", size="medium"),
|
||||
"code": 'Tag("Tag", variant="primary", size="medium")',
|
||||
},
|
||||
"Range": {
|
||||
"instance": Range(min=0, max=100, value=50),
|
||||
"code": "Range(min=0, max=100, value=50)",
|
||||
},
|
||||
"RelativeTime": {
|
||||
"instance": RelativeTime(date="2021-01-01T00:00:00Z"),
|
||||
"code": 'RelativeTime(date="2021-01-01T00:00:00Z")',
|
||||
},
|
||||
# "SplitPanel": {
|
||||
# "instance": SplitPanel(
|
||||
# div("First panel"), div("Second panel"), orientation="vertical"
|
||||
# ),
|
||||
# "code": code(
|
||||
# 'SplitPanel(div("First panel"), div("Second panel"), orientation="vertical")'
|
||||
# ),
|
||||
# },
|
||||
},
|
||||
"elements": {
|
||||
"button": {
|
||||
"instance": btn,
|
||||
"code": """btn = button("Click me!")
|
||||
when('click', btn)(lambda: window.alert("Clicked!"))
|
||||
parentdiv.append(btn)
|
||||
""",
|
||||
},
|
||||
"div": {
|
||||
"instance": div(
|
||||
"This is a div",
|
||||
style={
|
||||
"text-align": "center",
|
||||
"margin": "0 auto",
|
||||
"background-color": "cornsilk",
|
||||
},
|
||||
),
|
||||
"code": 'div("This is a div", style={"text-align": "center", "margin": "0 auto", "background-color": "cornsilk"})',
|
||||
},
|
||||
"input": {"instance": inputs_div, "code": inputs_code},
|
||||
"grid": {
|
||||
"instance": grid(
|
||||
"30% 70%",
|
||||
[
|
||||
div("This is a grid", style={"background-color": "lightblue"}),
|
||||
p("with 2 elements", style={"background-color": "lightyellow"}),
|
||||
],
|
||||
),
|
||||
"code": 'grid([div("This is a grid")])',
|
||||
},
|
||||
"headers": {"instance": headers_div, "code": headers_code},
|
||||
"a": {
|
||||
"instance": a(
|
||||
"Click here for something awesome",
|
||||
href="https://pyscript.net",
|
||||
target="_blank",
|
||||
),
|
||||
"code": 'a("Click here for something awesome", href="https://pyscript.net", target="_blank")',
|
||||
},
|
||||
"br": {
|
||||
"instance": div([p("This is a paragraph"), br(), p("with a line break")]),
|
||||
"code": 'div([p("This is a paragraph"), br(), p("with a line break")])',
|
||||
},
|
||||
"img": {
|
||||
"instance": img(src="./giphy_winner.gif", style={"max-width": "200px"}),
|
||||
"code": 'img(src="./giphy_winner.gif", style={"max-width": "200px"})',
|
||||
},
|
||||
"code": {
|
||||
"instance": code("print('Hello, World!')"),
|
||||
"code": "code(\"print('Hello, World!')\")",
|
||||
},
|
||||
"p": {"instance": p("This is a paragraph"), "code": 'p("This is a paragraph")'},
|
||||
"small": {
|
||||
"instance": small("This is a small text"),
|
||||
"code": 'small("This is a small text")',
|
||||
},
|
||||
"strong": {
|
||||
"instance": strong("This is a strong text"),
|
||||
"code": 'strong("This is a strong text")',
|
||||
},
|
||||
},
|
||||
"markdown": {
|
||||
"markdown": {
|
||||
"instance": markdown(MARKDOWN_EXAMPLE),
|
||||
"code": f'markdown("""{MARKDOWN_EXAMPLE}""")',
|
||||
},
|
||||
},
|
||||
}
|
||||
31
pyscript.core/test/ui/gallery.html
Normal file
31
pyscript.core/test/ui/gallery.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>PyDom UI</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="../../dist/core.css">
|
||||
<script type="module" src="../../dist/core.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked@11.1.1/lib/marked.umd.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
|
||||
<!-- and it's easy to individually load additional languages -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/go.min.js"></script>
|
||||
|
||||
<script>hljs.highlightAll();</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, "system-ui", "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="mpy" src="./gallery.py" config="./pyscript.toml"></script>
|
||||
</body>
|
||||
</html>
|
||||
180
pyscript.core/test/ui/gallery.py
Normal file
180
pyscript.core/test/ui/gallery.py
Normal file
@@ -0,0 +1,180 @@
|
||||
try:
|
||||
from textwrap import dedent
|
||||
except ImportError:
|
||||
dedent = lambda x: x
|
||||
|
||||
import inspect
|
||||
|
||||
import shoelace
|
||||
import styles
|
||||
import tictactoe
|
||||
from markdown import markdown
|
||||
from pyscript import when, window
|
||||
from pyweb import pydom
|
||||
from pyweb.ui import elements as el
|
||||
|
||||
MAIN_PAGE_MARKDOWN = dedent(
|
||||
"""
|
||||
This gallery is a collection of demos using the PyWeb.UI library. There are meant
|
||||
to be examples of how to use the library to create GUI applications using Python
|
||||
only.
|
||||
|
||||
## How to use the gallery
|
||||
|
||||
Simply click on the demo you want to see and the details will appear on the right
|
||||
"""
|
||||
)
|
||||
|
||||
# First thing we do is to load all the external resources we need
|
||||
shoelace.load_resources()
|
||||
|
||||
|
||||
def add_demo(demo_name, demo_creator_cb, parent_div, source=None):
|
||||
"""Create a link to a component and add it to the left panel.
|
||||
|
||||
Args:
|
||||
component (str): The name of the component to add.
|
||||
|
||||
Returns:
|
||||
the component created
|
||||
|
||||
"""
|
||||
# Create the component link element
|
||||
div = el.div(el.a(demo_name, href="#"), style=styles.STYLE_LEFT_PANEL_LINKS)
|
||||
|
||||
# Create a handler that opens the component details when the link is clicked
|
||||
@when("click", div)
|
||||
def _change():
|
||||
if source:
|
||||
demo_div = el.grid("50% 50%")
|
||||
demo_div.append(demo_creator_cb())
|
||||
widget_code = markdown(dedent(f"""```python\n{source}\n```"""))
|
||||
demo_div.append(el.div(widget_code, style=styles.STYLE_CODE_BLOCK))
|
||||
else:
|
||||
demo_div = demo_creator_cb()
|
||||
demo_div.style["margin"] = "20px"
|
||||
write_to_main(demo_div)
|
||||
window.hljs.highlightAll()
|
||||
|
||||
# Add the new link element to the parent div (left panel)
|
||||
parent_div.append(div)
|
||||
return div
|
||||
|
||||
|
||||
def create_main_area():
|
||||
"""Create the main area of the right side of page, with the description of the
|
||||
demo itself and how to use it.
|
||||
|
||||
Returns:
|
||||
the main area
|
||||
|
||||
"""
|
||||
return el.div(
|
||||
[
|
||||
el.h1("PyWeb UI Gallery", style={"text-align": "center"}),
|
||||
markdown(MAIN_PAGE_MARKDOWN),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def create_markdown_app():
|
||||
"""Create the basic components page.
|
||||
|
||||
Returns:
|
||||
the main area
|
||||
|
||||
"""
|
||||
translate_button = shoelace.Button("Convert", variant="primary")
|
||||
markdown_txt_area = shoelace.TextArea(label="Use this to write your Markdown")
|
||||
result_div = el.div(style=styles.STYLE_MARKDOWN_RESULT)
|
||||
|
||||
@when("click", translate_button)
|
||||
def translate_markdown():
|
||||
result_div.html = markdown(markdown_txt_area.value).html
|
||||
|
||||
return el.div(
|
||||
[
|
||||
el.h2("Markdown"),
|
||||
markdown_txt_area,
|
||||
translate_button,
|
||||
result_div,
|
||||
],
|
||||
style={"margin": "20px"},
|
||||
)
|
||||
|
||||
|
||||
# ********** MAIN PANEL **********
|
||||
main_area = create_main_area()
|
||||
|
||||
|
||||
def write_to_main(content):
|
||||
main_area.html = ""
|
||||
main_area.append(content)
|
||||
|
||||
|
||||
def restore_home():
|
||||
write_to_main(create_main_area())
|
||||
|
||||
|
||||
def create_new_section(title, parent_div):
|
||||
basic_components_text = el.h3(
|
||||
title, style={"text-align": "left", "margin": "20px auto 0"}
|
||||
)
|
||||
parent_div.append(basic_components_text)
|
||||
parent_div.append(
|
||||
shoelace.Divider(style={"margin-top": "5px", "margin-bottom": "30px"})
|
||||
)
|
||||
return basic_components_text
|
||||
|
||||
|
||||
# ********** LEFT PANEL **********
|
||||
left_panel_title = el.h1("PyWeb.UI", style=styles.STYLE_LEFT_PANEL_TITLE)
|
||||
left_div = el.div(
|
||||
[
|
||||
left_panel_title,
|
||||
shoelace.Divider(style={"margin-bottom": "30px"}),
|
||||
el.h3("Demos", style=styles.STYLE_LEFT_PANEL_TITLE),
|
||||
]
|
||||
)
|
||||
|
||||
# Let's map the creation of the main area to when the user clocks on "Components"
|
||||
when("click", left_panel_title)(restore_home)
|
||||
|
||||
# ------ ADD DEMOS ------
|
||||
markdown_source = """
|
||||
translate_button = shoelace.Button("Convert", variant="primary")
|
||||
markdown_txt_area = shoelace.TextArea(label="Markdown",
|
||||
help_text="Write your Mardown here and press convert to see the result",
|
||||
)
|
||||
result_div = el.div(style=styles.STYLE_MARKDOWN_RESULT)
|
||||
@when("click", translate_button)
|
||||
def translate_markdown():
|
||||
result_div.html = markdown(markdown_txt_area.value).html
|
||||
|
||||
el.div([
|
||||
el.h2("Markdown"),
|
||||
markdown_txt_area,
|
||||
translate_button,
|
||||
result_div,
|
||||
])
|
||||
"""
|
||||
add_demo("Markdown", create_markdown_app, left_div, source=markdown_source)
|
||||
add_demo(
|
||||
"Tic Tac Toe",
|
||||
tictactoe.create_tic_tac_toe,
|
||||
left_div,
|
||||
source=inspect.getsource(tictactoe),
|
||||
)
|
||||
|
||||
left_div.append(shoelace.Divider(style={"margin-top": "5px", "margin-bottom": "30px"}))
|
||||
left_div.append(el.a("Examples", href="/test/ui/", style={"text-align": "left"}))
|
||||
|
||||
# ********** CREATE ALL THE LAYOUT **********
|
||||
grid = el.grid("minmax(100px, 200px) 20px auto", style={"min-height": "100%"})
|
||||
grid.append(left_div)
|
||||
grid.append(shoelace.Divider(vertical=True))
|
||||
grid.append(main_area)
|
||||
|
||||
pydom.body.append(grid)
|
||||
pydom.body.append(el.a("Back to the main page", href="/test/ui/", target="_blank"))
|
||||
pydom.body.append(el.a("Hidden!!!", href="/test/ui/", target="_blank", hidden=True))
|
||||
39
pyscript.core/test/ui/index.html
Normal file
39
pyscript.core/test/ui/index.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>PyDom UI</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="../../dist/core.css">
|
||||
<script type="module" src="../../dist/core.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked@11.1.1/lib/marked.umd.min.js"></script>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
|
||||
<!-- and it's easy to individually load additional languages -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/go.min.js"></script>
|
||||
<!-- SHOWLACE CUSTOM CSS -->
|
||||
<style>
|
||||
</style>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, "system-ui", "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"
|
||||
}
|
||||
|
||||
input:invalid {
|
||||
background-color: lightpink;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="mpy" src="./demo.py" config="./pyscript.toml"></script>
|
||||
</body>
|
||||
</html>
|
||||
8
pyscript.core/test/ui/pyscript.toml
Normal file
8
pyscript.core/test/ui/pyscript.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
packages = []
|
||||
|
||||
[files]
|
||||
"./examples.py" = "./examples.py"
|
||||
"./tictactoe.py" = "./tictactoe.py"
|
||||
"./styles.py" = "./styles.py"
|
||||
"./shoelace.py" = "./shoelace.py"
|
||||
"./markdown.py" = "./markdown.py"
|
||||
Reference in New Issue
Block a user