diff --git a/json-tree/README.md b/json-tree/README.md new file mode 100644 index 00000000..955d0653 --- /dev/null +++ b/json-tree/README.md @@ -0,0 +1,13 @@ +# json-tree + +This example demonstrates "dynamic components", wherein a component is created +and stored in a State var or returned from a computed var. Dynamic components can be +constructed imperatively from complex data without having to +use `rx.cond` or `rx.foreach` as is required for normal UI components. + +Although dynamic components can be less performant and affect SEO, they are more +flexible when rendering nested data and allow components to be constructed using +regular python code. + +To use the example app, paste in valid JSON and see the structure of the JSON +displayed as a nested `rx.data_list`. \ No newline at end of file diff --git a/json-tree/assets/favicon.ico b/json-tree/assets/favicon.ico new file mode 100644 index 00000000..166ae995 Binary files /dev/null and b/json-tree/assets/favicon.ico differ diff --git a/json-tree/json_tree/__init__.py b/json-tree/json_tree/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/json-tree/json_tree/json_tree.py b/json-tree/json_tree/json_tree.py new file mode 100644 index 00000000..9c5db82a --- /dev/null +++ b/json-tree/json_tree/json_tree.py @@ -0,0 +1,67 @@ +"""Render nested JSON data as a tree using `rx.data_list`""" + +import json + +import reflex as rx + + +class State(rx.State): + _json: str = "" + info: rx.Component = rx.text("Paste JSON data") + + @rx.var(cache=True) + def comp_json_view(self) -> rx.Component: + if not self._json: + return rx.text("No JSON data") + try: + obj = json.loads(self._json) + except json.JSONDecodeError as e: + return rx.text(f"Invalid JSON: {e}") + + def make_json(node) -> rx.Component: + children = [] + if isinstance(node, dict): + for key, value in node.items(): + children.append( + rx.data_list.item( + rx.data_list.label(key), + rx.data_list.value(make_json(value)), + ), + ) + return rx.data_list.root(*children, margin_bottom="1rem") + if isinstance(node, list): + for item in node: + children.append(rx.list_item(make_json(item))) + return rx.unordered_list(*children) + return rx.text(str(node)) + + return make_json(obj) + + @rx.event + def handle_paste_json(self, data: list[tuple[str, str]]): + self.info = rx.spinner() + yield + for mime_type, raw in data: + if mime_type == "text/plain": + try: + json.loads(raw) + self._json = raw + self.info = rx.text(f"Loaded {len(raw)} bytes of JSON data.") + except json.JSONDecodeError: + self._json = "" + break + + +def index() -> rx.Component: + return rx.fragment( + rx.vstack( + State.info, + State.comp_json_view, + padding_x="10px", + ), + rx.clipboard(on_paste=State.handle_paste_json), + ) + + +app = rx.App() +app.add_page(index) diff --git a/json-tree/requirements.txt b/json-tree/requirements.txt new file mode 100644 index 00000000..1de84caa --- /dev/null +++ b/json-tree/requirements.txt @@ -0,0 +1 @@ +reflex>=0.6.5 diff --git a/json-tree/rxconfig.py b/json-tree/rxconfig.py new file mode 100644 index 00000000..00369abe --- /dev/null +++ b/json-tree/rxconfig.py @@ -0,0 +1,5 @@ +import reflex as rx + +config = rx.Config( + app_name="json_tree", +) \ No newline at end of file