reactive-python / reactpy

It's React, but in Python
https://reactpy.dev
MIT License
7.85k stars 315 forks source link

Syntax similar to JSX #918

Open Archmonger opened 1 year ago

Archmonger commented 1 year ago

Current Situation

ReactJS has a method of HTML templating called JSX. We currently do not have an equivalent.

There is a WIP tag string PEP that proposes adding template literals to Python. We will need this to write JSX in Python in the same way it has been used to do so in Javascript.

While the tag string PEP remains in development, an interim transpiler has been proposed that would allow us to use a similar syntax before the PEP is accepted, and in older versions of Python. The syntax would look something like this:

button_style = {"font_style": "bold"}
button_text = "Click me!"
view = html @ f"<button on_click={lambda event: ...} style={button_text}>{button_text}</button>"
reveal_type(view)  # revealed type is: VdomDict

Proposed Actions

Aid in the development of this proposed transpiler and then develop an html tag function. An initial implementation of an html tag can be found here.

bitnom commented 1 year ago

If a transpiler is required to get the same sort of syntax as JSX, then how would template literals be any better than fstrings?

rmorshea commented 1 year ago

The main purpose of template literals is to have a dedicated syntax. Transpilers allow you to write any syntax you'd like in your Python files, and at least for JSX, that's already been done. The problem, and the reason I suspect Pyxl never saw wide adoption, is because it breaks all the tools you would normally use with a Python file (e.g. black, flake8, pylint, mypy, the list goes on...). You could as I proposed above, co-opt an existing syntax which is seen infrequently (e.g. tag @ f"..."), but without standardization, no one is going to create tooling that understands that bespoke syntax and provides highlighting and auto-completions.

rmorshea commented 1 year ago

I've published an import-time transpiler that implements that aforementioned tag string spec.

$ pip install tagstr
# tagstr: on
name = "world"
print @ f"hello {name}!"
hello  (<function <lambda> at 0x7f7a8586b920>, 'name', None, None) !

I don't think this is ready for production yet, but with some more work behind this I think it provides a promising alternative to the current, and rather disjointed, patterns for constructing views in ReactPy. Integrating this into ReactPy could also act as a compelling real-world use case for the Tag String PEP itself.

mtrunkbear commented 1 year ago

That could be PSX python syntax extension, or PYX python XML

mtrunkbear commented 1 year ago

@rmorshea Very good work bro, i was reading the tag string docs and tutorial and i think that is a very useful aproximation.

aranega commented 1 year ago

Just for the sake of "state of the art", there is also this project named packed that is similar to pyxl3, but the syntax looks a little bit more like like tsx syntax on some small aspects. I guess the same criticisms apply to this project about the lake of syntax highlight,...

zx013 commented 8 months ago

I use BeautifulSoup analyse xml, then it can work below.

@component
def DataList(items, functest=None, filter_by_priority=None, sort_by_priority=False):
    if filter_by_priority is not None:
        items = [i for i in items if i["priority"] <= filter_by_priority]
    if sort_by_priority:
        items = sorted(items, key=lambda i: i["priority"])
    list_item_elements = [f"""<li key="{i["id"]}">{i["text"]}</li>""" for i in items]
    test = "<sdfa>"
    return """
        <ul>
            abcdedfe
            {functest()}
            {"".join(list_item_elements)}
            {test}
            {test}
            def
            ""{""}""
            <div>test</div>
        </ul>
    """

@component
def TodoList(text=""):
    def functest():
        return ("test-----------------------abc")
    tasks = [
        {"id": 0, "text": f"{text} Make breakfast3", "priority": 0},
        {"id": 1, "text": f"{text} Feed the dog", "priority": 0},
        {"id": 2, "text": f"{text} Do laundry", "priority": 2},
    ]
    return """
        <section>
            <h1>My Todo List</h1>
            <DataList items={tasks} functest={functest} filter_by_priority={1} sort_by_priority={True} />
        </section>
    """

@component
def Test():
    a = 2
    b = 3
    return """
        <h1 id="1" class_name="abc">My Todo List {a + b} Test PSX</h1>
        <ul id="2">
            <li id="5">"Design {23} a cool new app</li>
            <li id="4">Build"</li>
            <li>"Share it with the world!</li>
        </ul>
        <TodoList class_name="abc" />
        <TodoList text="abcd">
            <div>abc</div>
        </TodoList>
        <img
            src="https://picsum.photos/id/237/500/300"
            class="img-fluid"
            style="width: 50%; margin-left: 25%;"
            alt="Billie Holiday"
            tabindex="0"
        />
    """
hasansezertasan commented 8 months ago

There is a project that this issue brought to my mind.

RudreshVeerkhare/ReactPy: React implementation in Python 3, which runs on the client-side.

I have never used this project, but I have examined its examples. It allows creating files with the .pyx extension and in these files, you can write HTML with Python, without doing any string formatting.

And I also want to highlight these resources:

And finally, I love Python syntax and hate micro-syntaxes (Jinja counts).

zx013 commented 8 months ago

There is a project that this issue brought to my mind.

RudreshVeerkhare/ReactPy: React implementation in Python 3, which runs on the client-side.

I have never used this project, but I have examined its examples. It allows creating files with the .pyx extension and in these files, you can write HTML with Python, without doing any string formatting.

And I also want to highlight these resources:

And finally, I love Python syntax and hate micro-syntaxes (Jinja counts).

ReactPy is client side but reactpy is server side. There are two ways to analyze pyx, BeautifulSoup and pypeg2. Then how to use js/ts. component library may be important, reactpy get the easy way get it.

hasansezertasan commented 8 months ago

ReactPy is client side but reactpy is server side. There are two ways to analyze pyx, BeautifulSoup and pypeg2. Then how to use js/ts. component library may be important, reactpy get the easy way get it.

Yep, thanks for informing 🤩.

hasansezertasan commented 8 months ago

I just discovered another package that lets you write HTML inside python, check it out: twidi/mixt: Write html components directly in python and you have a beautiful but controversial MIXTure

Archmonger commented 8 months ago

I can see there's a pretty heavy want for a JSX style syntax for ReactPy.

Ideally we would have added support for this once Python and code editors (such as VS Code) add support for syntax highlighting on strings. But perhaps we should release support for it for early adopters to test it out.

arindampradhan commented 4 months ago

Any progress related to this

Need a transpiler with .pyx extension

Currently rust has it already implemented in their yew framework https://yew.rs/docs/concepts/html Javascript has jsx already

There will be a need for transpilers so that a python file can import json, css as string Without a transpiler this project this library is very hard to use

(
    <div>
        <div data-key="abc"></div>
        <div class="parent">
            <span class="child" value="anything"></span>
            <label for="first-name">{ "First Name" }</label>
            <input type="text" id="first-name" value="placeholder" />
            <input type="checkbox" checked=true />
            <textarea value="write a story" />
            <select name="status">
                <option selected=true disabled=false value="">{ "Selected" }</option>
                <option selected=false disabled=true value="">{ "Unselected" }</option>
            </select>
        </div>
    </div>
)

With transpiler

import reactpy import render

def hello_world():
    return <h1>Hello world</h1>

render(hello_world, '#root')

Should transpile to

```py
import reactpy import render

def hello_world():
    return h('h1', 'Hello world')

render(hello_world, '#root')