{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "af105bae",
   "metadata": {},
   "source": [
    "# Your first component\n",
    "\n",
    "Let's create a first UI component with Pret. We will start simple, with the \"Hello World\" of UI components: a Todo list.\n",
    "\n",
    "Pret is a declarative UI library, which means that you describe the UI you want, and Pret takes care of rendering it for you.\n",
    "Under the hood, we use React and React libraries to render the UI.\n",
    "\n",
    "## Composing components\n",
    "\n",
    "Our app should be able to display a list of todos, where each todo is described by a text and a boolean indicating whether it is done or not.\n",
    "Let's use Joy's Checkbox for this:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f4fe045c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.pret+json": {
       "detach": false,
       "version_major": 0,
       "version_minor": 0,
       "view_data": {
        "chunk_idx": 0,
        "marshaler_id": "62eee33664264891ac6a7ef943cfba1d"
       }
      },
      "text/plain": [
       "<pret.render.Renderable object at 0x10587f0d0>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from pret_joy import Checkbox\n",
    "\n",
    "Checkbox(\n",
    "    label=\"My first todo\",\n",
    "    checked=True,\n",
    "    sx={\"m\": 1},\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "072e2c1c",
   "metadata": {},
   "source": [
    "\n",
    "Great ! We successfully declared and rendered our first component. Let's make it a list. We will use the Stack component to stack multiple components vertically. To compose components, we pass checkboxes as positional arguments (or a list) to the Stack component, and Pret will render them as children of the Stack component.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "8e31fa0c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.pret+json": {
       "detach": false,
       "version_major": 0,
       "version_minor": 0,
       "view_data": {
        "chunk_idx": 1,
        "marshaler_id": "62eee33664264891ac6a7ef943cfba1d"
       }
      },
      "text/plain": [
       "<pret.render.Renderable object at 0x1063dcc40>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from pret_joy import Checkbox, Stack\n",
    "\n",
    "Stack(\n",
    "    Checkbox(label=\"My first todo\", checked=True),\n",
    "    Checkbox(label=\"My second todo\", checked=False),\n",
    "    sx={\"m\": 1},\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6dbc4e38",
   "metadata": {},
   "source": [
    "\n",
    "Instead of hardcoding the todos, we can use a list of todos and a loop to render them:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "704e25d5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.pret+json": {
       "detach": false,
       "version_major": 0,
       "version_minor": 0,
       "view_data": {
        "chunk_idx": 2,
        "marshaler_id": "62eee33664264891ac6a7ef943cfba1d"
       }
      },
      "text/plain": [
       "<pret.render.Renderable object at 0x1063dda80>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from pret_joy import Checkbox, Stack\n",
    "\n",
    "todos = [\n",
    "    {\"text\": \"My first todo\", \"done\": True},\n",
    "    {\"text\": \"My second todo\", \"done\": False},\n",
    "]\n",
    "\n",
    "Stack(\n",
    "    [Checkbox(label=todo[\"text\"], checked=todo[\"done\"]) for todo in todos],\n",
    "    spacing=2,\n",
    "    sx={\"m\": 1},\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74f5c136",
   "metadata": {},
   "source": [
    "\n",
    "We can turn this into a TodoList component, so that we can reuse it later:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "867cc110",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.pret+json": {
       "detach": false,
       "version_major": 0,
       "version_minor": 0,
       "view_data": {
        "chunk_idx": 3,
        "marshaler_id": "62eee33664264891ac6a7ef943cfba1d"
       }
      },
      "text/plain": [
       "<pret.render.Renderable object at 0x1063dd990>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from pret_joy import Checkbox, Stack\n",
    "\n",
    "from pret import component\n",
    "\n",
    "\n",
    "@component\n",
    "def TodoList(todos):\n",
    "    return Stack(\n",
    "        [Checkbox(label=todo[\"text\"], checked=todo[\"done\"]) for todo in todos],\n",
    "        spacing=2,\n",
    "        sx={\"m\": 1},\n",
    "    )\n",
    "\n",
    "\n",
    "TodoList(todos=todos)  # (1)!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa0a1d21",
   "metadata": {},
   "source": [
    "\n",
    "1. Here, `todos` are not children components but parameters of the `TodoList` component, also known as `props` in React, so we pass them as keyword arguments. In fact, passing them as positional arguments would raise an error.\n",
    "\n",
    "You can also write `@component()` when you want to pass options to the decorator. For components with expensive renders and stable props, `@component(memo=True)` wraps the generated React component with `React.memo`.\n",
    "\n",
    "## Reacting to events\n",
    "\n",
    "Now that we have a list of todos, we want to be able to mark them as done or not. We can use the `on_change` event of the Checkbox component to react to changes. For now, let's just make a popup appear when a todo is checked or unchecked.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "f39324e1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.pret+json": {
       "detach": false,
       "version_major": 0,
       "version_minor": 0,
       "view_data": {
        "chunk_idx": 4,
        "marshaler_id": "62eee33664264891ac6a7ef943cfba1d"
       }
      },
      "text/plain": [
       "<pret.render.Renderable object at 0x1063dcbe0>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from pret_joy import Checkbox, Stack\n",
    "\n",
    "\n",
    "def on_change(event):\n",
    "    checked = event.target.checked\n",
    "    alert(f\"Todo {'checked' if checked else 'unchecked'}\")\n",
    "\n",
    "\n",
    "Checkbox(\n",
    "    label=\"My first todo\",\n",
    "    checked=True,\n",
    "    on_change=on_change,\n",
    "    sx={\"m\": 1},\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0dc01ca4",
   "metadata": {},
   "source": [
    "\n",
    "## Adding state\n",
    "\n",
    "Our app is still a bit static : you may have noticed that you cannot change the value of the checboxes. We need to add state to our app to keep track of the todos' state. Let's start simple by making a Counter component that increments a counter each time a button is clicked. We can use the `use_state` hook, which allows us to create a state variable that will persist across renders (calls of our component) and trigger a re-render when its value changes.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "adb236d1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.pret+json": {
       "detach": false,
       "version_major": 0,
       "version_minor": 0,
       "view_data": {
        "chunk_idx": 5,
        "marshaler_id": "62eee33664264891ac6a7ef943cfba1d"
       }
      },
      "text/plain": [
       "<pret.render.Renderable object at 0x1063de920>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from pret_joy import Button, Stack, Typography\n",
    "\n",
    "from pret import component, use_state\n",
    "\n",
    "\n",
    "@component\n",
    "def Counter():\n",
    "    count, set_count = use_state(0)\n",
    "\n",
    "    def increment(event):\n",
    "        set_count(count + 1)\n",
    "\n",
    "    return Stack(\n",
    "        [\n",
    "            Button(\"Increment\", on_click=increment),\n",
    "            Typography(f\"Count: {count}\"),\n",
    "        ],\n",
    "        spacing=2,\n",
    "        sx={\"m\": 1},\n",
    "    )\n",
    "\n",
    "\n",
    "Counter()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b511a6a",
   "metadata": {},
   "source": [
    "\n",
    "As you can see, every time you click the button, the state changes which triggers a re-render of the component. This is how we can make our TodoList component interactive. We will use the `use_state` hook to keep track of the todos' state.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "18540962",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.pret+json": {
       "detach": false,
       "version_major": 0,
       "version_minor": 0,
       "view_data": {
        "chunk_idx": 6,
        "marshaler_id": "62eee33664264891ac6a7ef943cfba1d"
       }
      },
      "text/plain": [
       "<pret.render.Renderable object at 0x1063dfb80>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from pret_joy import Checkbox, Stack\n",
    "\n",
    "from pret import component\n",
    "\n",
    "todos = [\n",
    "    {\"text\": \"My first todo\", \"done\": True},\n",
    "    {\"text\": \"My second todo\", \"done\": False},\n",
    "]\n",
    "\n",
    "\n",
    "@component\n",
    "def TodoList(todos):\n",
    "    todos, set_todos = use_state(todos)\n",
    "\n",
    "    def on_change(event, index):\n",
    "        new_todos = list(todos)\n",
    "        new_todos[index] = {**todos[index], \"done\": event.target.checked}\n",
    "        set_todos(new_todos)\n",
    "\n",
    "    return Stack(\n",
    "        [\n",
    "            Checkbox(\n",
    "                label=todo[\"text\"],\n",
    "                checked=todo[\"done\"],\n",
    "                on_change=(lambda index: lambda event: on_change(event, index))(index),\n",
    "            )\n",
    "            for index, todo in enumerate(todos)\n",
    "        ],\n",
    "        spacing=2,\n",
    "        sx={\"m\": 1},\n",
    "    )\n",
    "\n",
    "\n",
    "TodoList(todos=todos)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
