Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement v-on and v-bind #63

Merged
merged 2 commits into from Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 20 additions & 0 deletions examples/PlainPython/Menu/README.md
@@ -0,0 +1,20 @@
# Menu example

This example illustrates how to implement a dropdown menu in vuetify using `v-slot:activator` with `v-bind` and `v-on` like this one:

```html
<template>
<v-menu>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on"> Dropdown </v-btn>
</template>
<v-list>
<v-list-item v-for="(item, index) in items" :key="index">
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</template>
```

And demonstrate how to pass variables from Vue to a Python method.
31 changes: 31 additions & 0 deletions examples/PlainPython/Menu/app.py
@@ -0,0 +1,31 @@
from trame import state
from trame.html import vuetify
from trame.layouts import SinglePage

layout = SinglePage("Menu example")

state.menu_items = ["one", "two", "three"]


def print_item(item):
print("Clicked on", item)


with layout.toolbar:
vuetify.VSpacer()
with vuetify.VMenu():
with vuetify.Template(v_slot_activator="{ on, attrs }"):
with vuetify.VBtn(icon=True, v_bind="attrs", v_on="on", v_on_click="test"):
vuetify.VIcon("mdi-dots-vertical")
with vuetify.VList(), vuetify.VListItem(
v_for="(item, i) in menu_items",
key="i",
value=["item"],
):
vuetify.VBtn(
"{{ item }}",
click=(print_item, "[item]"),
)

if __name__ == "__main__":
layout.start()
94 changes: 72 additions & 22 deletions trame/html/__init__.py
Expand Up @@ -15,6 +15,35 @@ def py2js_key(key):
return key.replace("_", "-")


def js2py_key(key):
return key.replace("-", "_")


def build_attr_names(name_prefix, key_names, kwargs):
"""Used to generate a list of attr_names with a common name_prefix."""
attr_names = []
for key_name in key_names:
safe_name_prefix = js2py_key(name_prefix)
safe_name = js2py_key(key_name).replace(".", "_")
if "<name>" in safe_name:
safe_header, safe_tail = safe_name.split("<name>")
header, tail = key_name.split("<name>")
for key in kwargs:
if key.startswith(header):
dyna_name = key[len(header) : -len(tail)]
attr_names.append(
(
f"{safe_name_prefix}_{safe_header}{dyna_name}{safe_tail}",
f"{name_prefix}:{header}{dyna_name}{tail}",
)
)
else:
attr_names.append(
(f"{safe_name_prefix}_{safe_name}", f"{name_prefix}:{key_name}")
)
return attr_names


class ElementContextManager:
def __init__(self):
self.element_stack = []
Expand All @@ -33,6 +62,36 @@ def add_child(self, elem):

HTML_CTX = ElementContextManager()

key_names = [
"delete",
"down",
"enter",
"esc",
"left",
"right",
"space",
"tab",
"up",
]
v_on_names = [
"click.capture",
"click.once",
"click.prevent",
"click.prevent.self",
"click.self.prevent",
"click.self",
"click.stop.prevent",
"click.stop",
"click",
"scroll.passive",
"scroll",
"submit.prevent",
"submit",
*["keyup." + k for k in key_names],
*["keydown." + k for k in key_names],
]
v_bind_names = ["class", "style"]


class AbstractElement:
"""
Expand Down Expand Up @@ -72,6 +131,8 @@ class AbstractElement:
:param v_if: See |vue_doc_link| for more info
:param v_show: See |vue_doc_link| for more info
:param v_for: See |vue_doc_link| for more info
:param v_on: See |vue_doc_link| for more info
:param v_bind: See |vue_doc_link| for more info
:param key: See |vue_doc_link| for more info

Events - See |mdn_event_link| for more info
Expand Down Expand Up @@ -113,17 +174,21 @@ def __init__(self, _elem_name, children=None, **kwargs):
"style",
("key", ":key"),
# default vue.js directives
"v_text",
"v_html",
"v_show",
"v_if",
"v_else",
"v_bind",
"v_else_if",
"v_else",
"v_for",
"v_html",
"v_if",
"v_model",
"v_pre",
"v_on",
"v_once",
"v_pre",
"v_show",
"v_text",
]
self._attr_names += build_attr_names("v-on", v_on_names, kwargs)
self._attr_names += build_attr_names("v-bind", v_bind_names, kwargs)
self._event_names += [
"click",
"mousedown",
Expand Down Expand Up @@ -505,22 +570,7 @@ class Template(AbstractElement):
def __init__(self, children=None, **kwargs):
super().__init__("template", children, **kwargs)
self._attr_names += ["v_slot"]
for slot_name in Template.slot_names:
safe_name = slot_name.replace("-", "_").replace(".", "_")
if "<name>" in safe_name:
safe_header, safe_tail = safe_name.split("<name>")
header, tail = slot_name.split("<name>")
for key in kwargs:
if key.startswith(header):
dyna_name = key[len(header) : -len(tail)]
self._attr_names.append(
(
f"v_slot_{safe_header}{dyna_name}{safe_tail}",
f"v-slot:{header}{dyna_name}{tail}",
)
)
else:
self._attr_names.append((f"v_slot_{safe_name}", f"v-slot:{slot_name}"))
self._attr_names += build_attr_names("v-slot", Template.slot_names, kwargs)


class StateChange(AbstractElement):
Expand Down