{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import panel as pn\n",
    "pn.extension()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "``Tabs`` allow switching between multiple objects by clicking on the corresponding tab header. Tab labels may be defined explicitly as part of a tuple or will be inferred from the ``name`` parameter of the tab's contents. Like ``Column`` and ``Row``, ``Tabs`` has a list-like API with methods to ``append``, ``extend``, ``clear``, ``insert``, ``pop``, ``remove`` and ``__setitem__``, which make it possible to interactively update and modify the tabs.\n",
    "\n",
    "#### Parameters:\n",
    "\n",
    "For details on other options for customizing the component see the [layout](../../how_to/layout/index.md) and [styling](../../how_to/styling/index.md) how-to guides.\n",
    "\n",
    "* **``active``** (int, default=0): The index of the currently selected tab. Updates when a tab is selected and may also be set programmatically to flip between tabs.\n",
    "* **``dynamic``** (boolean, default=False): Dynamically populate only the active Tab.\n",
    "* **``closable``** (boolean, default=False): Whether it should be allowed to close tabs using the GUI, which deletes them from the list of objects.\n",
    "* **``objects``** (list): The list of objects to display in the Column. Should not generally be modified directly except when replaced in its entirety.\n",
    "* **``tabs_location``** (str, default='above'): The location of the tabs relative to the content. Must be one of 'left', 'right', 'below' or 'above'.\n",
    "\n",
    "___"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tabs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A ``Tabs`` layout can either be instantiated as empty and be populated after the fact, or using a list of objects provided as positional arguments. If the objects are not already Panel components they will each be converted to one using the ``pn.panel`` conversion method. Unlike other panel ``Tabs`` also accepts tuples to specify the title of each tab, if no name is supplied explicitly the name of the underlying object will be used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.plotting import figure\n",
    "\n",
    "p1 = figure(width=300, height=300, name='Scatter')\n",
    "p1.scatter([0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 2, 1, 0])\n",
    "\n",
    "p2 = figure(width=300, height=300, name='Line')\n",
    "p2.line([0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 2, 1, 0])\n",
    "\n",
    "tabs = pn.Tabs(('Scatter', p1), p2)\n",
    "tabs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``Tabs`` objects should never be modified directly. Instead, it is recommended to modify tabs using the provided methods, except when replacing the list of ``objects`` entirely.  Using the methods ensures that the rendered views of the ``Tabs`` are rerendered in response to the change, but even more importantly it ensures the tab titles are kept in sync with the objects. As a simple example we might add an additional widget to the ``tabs`` using the append method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p3 = figure(width=300, height=300, name='Square')\n",
    "p3.square([0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 2, 1, 0], size=10)\n",
    "\n",
    "tabs.append(p3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On a live server or in a notebook the `tabs` displayed above will dynamically expand to include the new tab. To see the effect in a statically rendered page, we will display the tabs a second time:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tabs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### ``active``"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In addition to being able to modify the ``objects`` using methods we can also get and set the currently ``active`` tab as an integer, which will update any rendered views of the object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(tabs.active)\n",
    "tabs.active = 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### ``dynamic``\n",
    "\n",
    "When enabled the dynamic option ensures that only the active tab is actually rendered, only when switching to a new Tab are the contents loaded. This can be very helpful in a server context or notebook context when displaying a lot of tabs or when rendering the individual objects are very large or expensive to render. Note however that without a live server the contents of the non-active tab will never load:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tabs = pn.Tabs(p1, p2, p3, dynamic=True)\n",
    "\n",
    "tabs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you want the `Tabs` to be completely lazy when rendering some output you can leverage a [ParamFunction or ParamMethod](../../user_guide/Param.ipynb) to ensure that the output is not computed until you navigate to the tab:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "import numpy as np\n",
    "\n",
    "def plot():\n",
    "    time.sleep(1) # some long running calculation\n",
    "    np.random.seed(tabs.active)\n",
    "    xs, ys = np.random.randn(2, 100)\n",
    "    p = figure(width=300, height=300, name=f'Scatter Seed {tabs.active}')\n",
    "    p.scatter(xs, ys)\n",
    "    return p\n",
    "\n",
    "p1 = pn.param.ParamFunction(plot, lazy=True, name='Seed 0')\n",
    "p2 = pn.param.ParamFunction(plot, lazy=True, name='Seed 1')\n",
    "p3 = pn.param.ParamFunction(plot, lazy=True, name='Seed 2')\n",
    "\n",
    "tabs = pn.Tabs(p1, p2, p3, dynamic=True)\n",
    "\n",
    "tabs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### ``closable``\n",
    "\n",
    "``Tabs`` may also be initialized as ``closable``, which provides an `x` widget in the GUI that makes it possible to remove tabs and therefore remove them from the list of ``objects``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tabs = pn.Tabs(\n",
    "    ('red', pn.Spacer(styles=dict(background='red'), width=100, height=100)),\n",
    "    ('blue', pn.Spacer(styles=dict(background='blue'), width=100, height=100)),\n",
    "    ('green', pn.Spacer(styles=dict(background='green'), width=100, height=100)),\n",
    "    closable=True\n",
    ")\n",
    "\n",
    "tabs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### ``tabs_location``\n",
    "\n",
    "Lastly, it is possible to modify the location of the tabs header relative to the content using the ``tabs_location`` parameter:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.Row(tabs, tabs.clone(active=1, tabs_location='right'), tabs.clone(active=2, tabs_location='below'), tabs.clone(tabs_location='left'))"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
