diff options
Diffstat (limited to 'docs/content/ui')
-rw-r--r-- | docs/content/ui/accordion.md | 43 | ||||
-rw-r--r-- | docs/content/ui/index.md | 58 | ||||
-rw-r--r-- | docs/content/ui/linkedlist.md | 19 | ||||
-rw-r--r-- | docs/content/ui/menu.md | 112 | ||||
-rw-r--r-- | docs/content/ui/popover.md | 204 | ||||
-rw-r--r-- | docs/content/ui/reldate.md | 58 | ||||
-rw-r--r-- | docs/content/ui/tabs.md | 162 |
7 files changed, 656 insertions, 0 deletions
diff --git a/docs/content/ui/accordion.md b/docs/content/ui/accordion.md new file mode 100644 index 0000000..8676fd5 --- /dev/null +++ b/docs/content/ui/accordion.md @@ -0,0 +1,43 @@ +--- +title: Accordion +description: Component for grouping <details> HTML elements where only one of them can be open at the same time. +--- + +<Header title="Accordion" section="UI components"> + Component for grouping <code>details</code> HTML elements where only one of them can be open at the same time. +</Header> + +An accordion is a vertically stacked group of collapsible sections. HTML has already a native element for this, the `<details>` element, but it doesn't support the "only one open at a time" behavior, so we need to add some JS to make it work, and that's what this component does. + +If you don't need to ensure only one section is open at a time, you don't need this component at all, just use the `<details>` element directly. + +<ExampleTabs + prefix="demo" + :panels="{ + 'Result': 'ui.Accordion.DemoResult', + 'HTML': 'ui.Accordion.DemoHTML', + 'CSS': 'ui.Accordion.DemoCSS', + }" +/> + + +The `Accordion` is a simple wrapper plus some JS logic, so it doesn't uses any arguments and it's as accesible as the `details` element you put inside. + + +## Events + +The `Accordion` doesn't emit or listen to any events, but the `<details>` elements inside do. + +In addition to the usual events supported by HTML elements, the `<details>` element supports the `toggle` event, which is dispatched to the `<details>` element whenever its state changes between open and closed. The `Accordion` component listen to it to be able to close the other `<details>` elements when one is opened. + +The `toggle` event is sent *after* the state is changed, although if the state changes multiple times before the browser can dispatch the event, the events are coalesced so that only one is sent. + +```js +details.addEventListener("toggle", (event) => { + if (details.open) { + /* the element was toggled open */ + } else { + /* the element was toggled closed */ + } +}); +``` diff --git a/docs/content/ui/index.md b/docs/content/ui/index.md new file mode 100644 index 0000000..0a406a4 --- /dev/null +++ b/docs/content/ui/index.md @@ -0,0 +1,58 @@ +--- +title: UI components +description: Unstyled, fully accessible UI components, to integrate with your projects. +--- + +<Header title="UI components" :section="false"> + Unstyled, fully accessible UI components, to integrate with your projects. +</Header> + +<div class="cd-cards not-prose"> + <a class="card" href="/ui/tabs"> + <h2>Tabs</h2> + <img src="/static/img/ui-tabs.png" /> + </a> + <a class="card" href="/ui/popover"> + <h2>Popover</h2> + <img src="/static/img/ui-popover.png" /> + </a> + <a class="card" href="/ui/menu"> + <h2>Menu</h2> + <img src="/static/img/ui-menu.png" /> + </a> + <a class="card" href="/ui/accordion"> + <h2>Accordion</h2> + <img src="/static/img/ui-accordion.png" /> + </a> + <a class="card" href="/ui/linkedlist"> + <h2>LinkedList</h2> + <img src="/static/img/ui-linkedlist.png" /> + </a> + <a class="card" href="/ui/reldate"> + <h2>RelDate</h2> + <img src="/static/img/ui-reldate.png" /> + </a> +</div> + + +## How to use + +1. Install the `jinjax-ui` python library doing + + ```bash + pip install jinjax-ui + ``` + +2. Add it to your *JinjaX* catalog: + + ```python + import jinjax_ui + + catalog.add_folder(jinjax_ui.components_path, prefix="") + ``` + +3. Use the UI components in your components/templates: + + ```html+jinja + <Popover> ... </Popover> + ```
\ No newline at end of file diff --git a/docs/content/ui/linkedlist.md b/docs/content/ui/linkedlist.md new file mode 100644 index 0000000..ac3fce2 --- /dev/null +++ b/docs/content/ui/linkedlist.md @@ -0,0 +1,19 @@ +--- +title: Linked Lists +description: A component to select multiple items from a long list, while keeping them sorted. +--- + +<Header title="Linked Lists" section="UI components"> + A component to select multiple items from a long list, while keeping them sorted. +</Header> + +<ExampleTabs + prefix="demo" + :panels="{ + 'Result': 'ui.LinkedList.DemoResult', + 'HTML': 'ui.LinkedList.DemoHTML', + 'CSS': 'ui.LinkedList.DemoCSS', + }" +/> + +<Callout>The checkboxes above are displayed so you can see how they get checked/unchecked. If you might want to hide them with CSS, the component will keep working as usual, but they must be present.</Callout> diff --git a/docs/content/ui/menu.md b/docs/content/ui/menu.md new file mode 100644 index 0000000..12539d5 --- /dev/null +++ b/docs/content/ui/menu.md @@ -0,0 +1,112 @@ +--- +title: Menu (Dropdown) +description: Displays a list of options that a user can choose with robust support for keyboard navigation. Built using the Popover API. +--- + +<Header title="Menu" section="UI components"> + Displays a list of options that a user can choose with robust support for + keyboard navigation. Built using the Popover API. +</Header> + +<ExampleTabs + prefix="menu-demo" + :panels="{ + 'Result': 'ui.Menu.DemoResult', + 'HTML': 'ui.Menu.DemoHTML', + 'CSS': 'ui.Menu.DemoCSS', + }" +/> + +**Note:** This component does not handle keyboard shortcuts, here are shown only as an example. + +Menus are built using the `Menu`, `MenuButton`, `MenuItem`, and `MenuItemSub` components. Clicking on menu button or activating it with the keyboard will show the corresponding menu. + +```html+jinja +<MenuButton>Open menu</<MenuButton> +<Menu> + <MenuItem> item1 </MenuItem> 〈-- Regular item + <MenuItem> item2 </MenuItem> + <MenuItem> item3 </MenuItem> + <MenuItemSub> item4 〈----------- An item with a submenu + <Menu> ... </Menu>〈----------- Submenu + </MenuItemSub> +</Menu> +``` + +A `Menu` starts hidden on page load by having `display:none` set on it (the Popover API does it automatically). To show/hide the menu, you need to add a `MenuButton`. + +When a `Menu` is shown, it has `display:none` removed from it and it is put into the top layer so, unlike just using `position: absolute`, it's guaranteed that it will sit on top of all other page content. + + +## Anchor positioning + +By default, the menu appears centered in the layout view, but this component allows you to position it relative to an specific element in the page, using the `anchor` and `anchor-to` attributes. + +`anchor` is the ID of the element used as a reference, and `anchor-to` which side of the anchor to use: "top", "bottom", "right", or "left"; with an optional postfix of "start" or "end" ("center" is the default). + +<p> + <img src="/static/img/anchors.png" alt="Anchor positioning" + width="595" height="324" style="display:block;margin:60px auto;" /> +</p> + +The positioning is done every time the menu opens, but you can trigger the re-position, for example, on windows resizing, by calling the `jxui-popover/setPosition(menu)` function. + + +## Styling states + +| CSS selector | Description +| ----------------------- | -------------- +| `.ui-menu` | Every menu has this class +| `.ui-menu:popover-open` | This pseudo-class matches only menus that are currently being shown +| `::backdrop` | This pseudo-element is a full-screen element placed directly behind showing menu elements in the top layer, allowing effects to be added to the page content behind the menu(s) if desired. You might for example want to blur out the content behind the menu to help focus the user's attention on it + +To animate a menu, follow the [Animating popovers section](/headless/popover#animating-popovers) in the Popover page. + + +## Component arguments + +### MenuButton + +| Argument | Type | Default | Description +| --------------- | --------- | ---------- | -------------- +| `target` | `str` | | Required. The ID of the linked `Popover` component. +| `action` | `str` | `"toggle"` | `"open"`, `"close"`, or `"toggle"`. +| `tag` | `str` | `"button"` | HTML tag of the component. + +### Menu + +| Argument | Type | Default | Description +| ------------ | ----- | -------- | -------------- +| `mode` | `str` | `"auto"` | `"auto"` or `"manual"`. +| `anchor` | `str` | | ID of the element used as an anchor +| `anchor-to` | `str` | | Which side/position of the anchor to use: "**top**", "**bottom**", "**right**", or "**left**"; with an optional postfix of "**start**", "**end**", "**center**". +| `tag` | `str` | `"div"` | HTML tag of the component. + +### MenuItem + +| Argument | Type | Default | Description +| ------------ | ----- | -------- | -------------- +| `mode` | `str` | `"auto"` | `"auto"` or `"manual"`. + +### MenuSubItem + +| Argument | Type | Default | Description +| ------------ | ----- | -------- | -------------- +| `mode` | `str` | `"auto"` | `"auto"` or `"manual"`. + + +## Accessibility notes + +### Mouse interaction + +- Clicking a `PopButton` will trigger the button action (open, close, or toggle state). + +- Clicking outside of a `Popover` will close *all* the `Popover` with `mode="auto"`. + + +### Keyboard interaction + +- Pressing the <kbd>Enter</kbd> or <kbd>Space</kbd> keys on a `PopButton` will trigger +the button action (open, close, or toggle state), and close *all* the `Popover` with `mode="auto"`. + +- Pressing the <kbd>Escape</kbd> key will close *all* the `Popover` with `mode="auto"`. diff --git a/docs/content/ui/popover.md b/docs/content/ui/popover.md new file mode 100644 index 0000000..673f96d --- /dev/null +++ b/docs/content/ui/popover.md @@ -0,0 +1,204 @@ +--- +title: Pop-over +description: A wrapper over the Popover API with anchor positioning. +--- + +<Header title="Pop-over" section="UI components"> + A wrapper over the Popover API with anchor positioning. +</Header> + +Pop-overs are powerful components with many use cases like edit menus, +custom notifications, content pickers, or help dialogs. + +They can also be used for big hideable sidebars, like a shopping cart or action panels. +Pop-overs are **always non-modal**. If you want to create a modal popoverover, a `Dialog` +component is the way to go instead. + +<ExampleTabs + prefix="demo" + :panels="{ + 'Result': 'ui.Popover.DemoResult', + 'HTML': 'ui.Popover.DemoHTML', + 'CSS': 'ui.Popover.DemoCSS', + }" +/> + +A `Popover` starts hidden on page load by having `display:none` set on it (the Popover API does it automatically). To show/hide the popover, you need to add some control `PopButton`s. + +When a popover is shown, it has `display:none` removed from it and it is put into the top layer so, unlike just using `position: absolute`, it's guaranteed that it will sit on top of all other page content. + + +## Anchor positioning + +By default, a popover appears centered in the layout view, but this component allows you to position it relative to an specific element in the page, using the `anchor` and `anchor-to` attributes. + +`anchor` is the ID of the element used as a reference, and `anchor-to` which side of the anchor to use: "top", "bottom", "right", or "left"; with an optional postfix of "start" or "end" ("center" is the default). + +<p> + <img src="/static//img/anchors.png" alt="Anchor positioning" + width="595" height="324" style="display:block;margin:60px auto;" /> +</p> + +The positioning is done every time the popover opens, but you can trigger the re-position, for example, on windows resizing, by calling the `jxui-popover/setPosition(popover)` function. + + +## Styling states + +| CSS selector | Description +| ------------------- | -------------- +| `[popover]` | Every popover has this attribute +| `:popover-open` | This pseudo-class matches only popovers that are currently being shown +| `::backdrop` | This pseudo-element is a full-screen element placed directly behind showing popover elements in the top layer, allowing effects to be added to the page content behind the popover(s) if desired. You might for example want to blur out the content behind the popover to help focus the user's attention on it + + +## Closing modes + +A `Popover` can be of two types: "auto" or "manual". This is controlled by the `mode` argument. + +| Argument | Description +| ------------------ | -------------- +| `mode="auto"` | The `Popover` will close automatically when the user clicks outside of it, or when presses the Escape key. +| `mode="manual"` | The `Popover` will not close automatically. It will only close when the user clicks on a linked `PopButton` with `action="close"` or `action="toggle"`. + +If the `mode` argument is not set, it defaults to "auto". + + +## `PopButton` actions + +A `PopButton` can have an `action` argument, which can be set to one of three values: "open", "close", or "toggle". This argument determines what happens to the target `Popover` when the button is clicked. + +| Argument | Description +| ----------------- | -------------- +| `action="open"` | Opens the target `Popover`. If the `Popover` is already open, it has no effect. +| `action="close"` | Closes the target `Popover`. If the `Popover` is already closed, it has no effect. +| `action="toggle"` | This is the default action. It toggles the target `Pop – opening it if it's closed and closing it if it's open. + + +## Animating popovers + +Popovers are set to `display:none;` when hidden and `display:block;` when shown, as well as being removed from / added to the [top layer](https://developer.mozilla.org/en-US/docs/Glossary/Top_layer). Therefore, for popovers to be animated, the `display` property [needs to be animatable]. + +[Supporting browsers](https://developer.mozilla.org/en-US/docs/Web/CSS/display#browser_compatibility) animate `display` flipping between `none` and another value of `display` so that the animated content is shown for the entire animation duration. So, for example: + +- When animating `display` from `none` to `block` (or another visible `display` value), the value will flip to `block` at `0%` of the animation duration so it is visible throughout. +- When animating `display` from `block` (or another visible `display` value) to `none`, the value will flip to `none` at `100%` of the animation duration so it is visible throughout. + +<Callout> +When animating using CSS transitions, `transition-behavior:allow-discrete` needs to be set to enable the above behavior. When animating with CSS animations, the above behavior is available by default; an equivalent step is not required. +</Callout> + + +### Transitioning a popover + +When animating popovers with CSS transitions, the following features are required: + +- `@starting-style` at-rule + + Provides a set of starting values for properties set on the popover that you want to transition from when it is first shown. This is needed to avoid unexpected behavior. By default, CSS transitions only occur when a property changes from one value to another on a visible element; they are not triggered on an element's first style update, or when the `display` type changes from `none` to another type. + +- `display` property + + Add `display` to the transitions list so that the popover will remain as `display:block` (or another visible `display` value) for the duration of the transition, ensuring the other transitions are visible. + +- `overlay` property + + Include `overlay` in the transitions list to ensure the removal of the popover from the top layer is deferred until the transition completes, again ensuring the transition is visible. + +- `transition-behavior` property + + Set `transition-behavior:allow-discrete` on the `display` and `overlay` transitions (or on the `transition` shorthand) to enable discrete transitions on these two properties that are not by default animatable. + +For example, let's say the styles we want to transition are `opacity` and `transform`: we want the popover to fade in or out while moving down or up. + +To achieve this, we set a starting state for these properties on the hidden state of the popover element (selected with the `[popover]` attribute selector) and an end state for the shown state of the popover (selected via the `:popover-open` pseudo-class). We also use the `transition` property to define the properties to animate and the animation's duration as the popover gets shown or hidden: + +```css +/*** Transition for the popover itself ***/ +[popover]:popover-open { + opacity: 1; + transform: scaleX(1); +} +[popover] { + transition: all 0.2s allow-discrete; + /* Final state of the exit animation */ + opacity: 0; + transform: translateY(-3rem); +} +[popover]:popover-open { + opacity: 1; + transform: translateY(0); +} +/* Needs to be after the previous [popover]:popover-open rule +to take effect, as the specificity is the same */ +@starting-style { + [popover]:popover-open { + opacity: 0; + transform: translateY(-3rem); + } +} + +/*** Transition for the popover's backdrop ***/ +[popover]::backdrop { + /* Final state of the exit animation */ + background-color: rgb(0 0 0 / 0%); + transition: all 0.2s allow-discrete; +} +[popover]:popover-open::backdrop { + background-color: rgb(0 0 0 / 15%); +} +@starting-style { + [popover]:popover-open::backdrop { + background-color: rgb(0 0 0 / 0%); + } +} +``` + +You can see a working example of this in the demo [at the beginning of the page](#startpage). + +<Callout> +Because popovers change from <code>display:none</code> to <code>display:block</code> each time they are shown, the popover transitions from its <code>@starting-style</code> styles to its <code>[popover]:popover-open</code> styles every time the entry transition occurs. When the popover closes, it transitions from its <code>[popover]:popover-open</code> state to the default <code>[popover]</code> state. + +<b>So it is possible for the style transition on entry and exit to be different.</b> +</Callout> + +<Callout type="note"> +This section was adapted from [Animating popovers](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API/Using#animating_popovers) +by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/MDN/Community/Roles_teams#contributor), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/). +</Callout> + + +## Component arguments + +### PopButton + +| Argument | Type | Default | Description +| --------------- | --------- | ---------- | -------------- +| `target` | `str` | | Required. The ID of the linked `Popover` component. +| `action` | `str` | `"toggle"` | `"open"`, `"close"`, or `"toggle"`. +| `tag` | `str` | `"button"` | HTML tag of the component. + +### Pop + +| Argument | Type | Default | Description +| ------------ | ----- | -------- | -------------- +| `mode` | `str` | `"auto"` | `"auto"` or `"manual"`. +| `anchor` | `str` | | ID of the element used as an anchor +| `anchor-to` | `str` | | Which side/position of the anchor to use: "**top**", "**bottom**", "**right**", or "**left**"; with an optional postfix of "**start**", "**end**", "**center**". +| `tag` | `str` | `"div"` | HTML tag of the component. + + +## Accessibility notes + +### Mouse interaction + +- Clicking a `PopButton` will trigger the button action (open, close, or toggle state). + +- Clicking outside of a `Popover` will close *all* the `Popover` with `mode="auto"`. + + +### Keyboard interaction + +- Pressing the <kbd>Enter</kbd> or <kbd>Space</kbd> keys on a `PopButton` will trigger +the button action (open, close, or toggle state), and close *all* the `Popover` with `mode="auto"`. + +- Pressing the <kbd>Escape</kbd> key will close *all* the `Popover` with `mode="auto"`. diff --git a/docs/content/ui/reldate.md b/docs/content/ui/reldate.md new file mode 100644 index 0000000..e3624c8 --- /dev/null +++ b/docs/content/ui/reldate.md @@ -0,0 +1,58 @@ +--- +title: Relative date +description: A component to convert datetimes to relative dates strings, such as "a minute ago", "in 2 hours", "yesterday", "3 months ago", etc. using JavaScript's Intl.RelativeTimeFormat API. +--- + +<Header title="Relative date" section="UI components"> +A component to convert datetimes to relative dates strings, +such as "a minute ago", "in 2 hours", "yesterday", "3 months ago", +etc. using JavaScript's <a class="link" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat" target="_blank">Intl.RelativeTimeFormat API</a>. +</Header> + +*Some examples (as if the datetime was `June 20th, 2024 6:30pm`)*: + +| Source | Relative date +| -----------------------------------------| -------------- +| `<RelDate datetime="2024-01-01"/>` | 6 months ago +| `<RelDate datetime="2024-06-19T18:30"/>` | yesterday +| `<RelDate datetime="2024-06-20T14:00"/>` | 5 hours ago +| `<RelDate datetime="2024-06-21T14:00"/>` | in 19 hours +| `<RelDate datetime="2024-06-30T10:00"/>` | next week +| `<RelDate datetime="1992-10-01"/>` | 32 years ago + + +## How it works + +The `RelDate` component is rendered as an empty `<time datetme="..." data-relative>` tag and, when the page load, the datetime is rendered by JavaScript. + +There is also a `MutationObserver` in place to render the datetime on any `<time datetme="..." data-relative>` inserted later to the page by JavaScript. + + +## Localization + +The locale used for the localization of the dates is, in order of priority: + +1. The optional `lang` attribute of the component; or +2. The `lang` attribute of the `<body>` tag + +Both can be a comma-separated lists of locales (e.g.: `"en-US,en-UK,en`). If none of these attributes exists, or if the locales are not supported by the browser, it fallsback to the default browser language. + +*Some examples (as if the datetime was `June 20th, 2024 6:30pm`)*: + +| Source | Relative date +| ---------------------------------------------------------------| -------------- +| `<RelDate datetime="2024-01-01" lang="it"/> ` | 6 mesi fa +| `<RelDate datetime="2024-06-19T18:30" lang="fr"/>` | hier +| `<RelDate datetime="2024-06-21T14:00" lang="es"/>` | dentro de 19 horas +| `<RelDate datetime="2024-06-21T14:00" lang="es-PE,es-ES,es"/>` | dentro de 19 horas + + +## Component arguments + +## RelDate + +| Argument | Type | Description +| -----------| ----- | -------------- +| `datetime` | `str` | Required. +| `lang` | `str` | Optional comma-separated list of locales to use for formatting. If not defined, the attribute `lang` of the `<body>` tag will be used. If that is also not defined, or none of the locales are supported by the browser, the default browser language is used +| `now` | `str` | Optional ISO-formatted date to use as the "present time". Useful for testing. diff --git a/docs/content/ui/tabs.md b/docs/content/ui/tabs.md new file mode 100644 index 0000000..094e271 --- /dev/null +++ b/docs/content/ui/tabs.md @@ -0,0 +1,162 @@ +--- +title: Tabs +description: Easily create accessible, fully customizable tab interfaces, with robust focus management and keyboard navigation support. +--- + +<Header title="Tabs" section="UI components"> + Easily create accessible, fully customizable tab interfaces, with robust focus management and keyboard navigation support. +</Header> + +<ExampleTabs + prefix="demo" + panels={{ { + 'Result': 'ui.Tabs.DemoResult', + 'HTML': 'ui.Tabs.DemoHTML', + 'CSS': 'ui.Tabs.DemoCSS', + } }} +/> + +Tabs are built using the `TabGroup`, `TabList`, `Tab`, and `TabPanel` components. Clicking on any tab or selecting it with the keyboard will activate the corresponding panel. + + +## Styling states + +| CSS selector | Description +| --------------- | -------------- +| `.ui-hidden` | Added to all `TabPanel` except the one that is active. +| `.ui-selected` | Added to the selected `Tab`. +| `.ui-disabled` | Added to disabled `Tab`s. + + +## Disabling a tab + +To disable a tab, use the disabled attribute on the `Tab` component. Disabled tabs cannot be selected with the mouse, and are also skipped when navigating the tab list using the keyboard. + +<Callout type="warning"> +Disabling tabs might be confusing for users. Instead, I reccomend you either remove it or explain why there is no content for that tab when is selected. +</Callout> + + +## Manually activating tabs + +By default, tabs are automatically selected as the user navigates through them using the arrow kbds. + +If you'd rather not change the current tab until the user presses <kbd>Enter</kbd> or <kbd>Space</kbd>, use the `manual` attribute on the `TabGroup` component. + +Remember to add styles to the `:focus` state of the tab so is clear to the user that the tab is focused. + +<ExampleTabs + prefix="manual" + panels={{{ + 'HTML': 'ui.Tabs.ManualHTML', + 'Result': 'ui.Tabs.ManualResult', + } }} +/> + +The manual prop has no impact on mouse interactions — tabs will still be selected as soon as they are clicked. + + +## Vertical tabs + +If you've styled your `TabList` to appear vertically, use the `vertical` attribute to enable navigating with the <kbd title="arrow up">↑</kbd> and <kbd title="arrow down">↓</kbd> arrow kbds instead of <kbd title="arrow left">←</kbd> and <kbd title="arrow right">→</kbd>, and to update the `aria-orientation` attribute for assistive technologies. + +<ExampleTabs + prefix="vertical" + panels={{ { + 'HTML': 'ui.Tabs.VerticalHTML', + 'Result': 'ui.Tabs.VerticalResult', + } }} +/> + + +## Controlling the tabs with a `<select>` + +Sometimes, you want to display a `<select>` element in addition to tabs. To do so, use the `TabSelect` and `TabOption` components. +A `TabSelect` component is a wrapper for a `<select>` element, and it accepts `TabOption` components as children. + +Note that a `TabSelect` **is not a replacement for a `TabList`**. For accessibility the `TabList` must be remain in your code, even if it's visually hidden. + +<ExampleTabs + prefix="select" + :panels="{ + 'HTML': 'ui.Tabs.SelectHTML', + 'Result': 'ui.Tabs.SelectResult', + }" +/> + + +## Component arguments + +### TabGroup + +| Argument | Type | Default | Description +| ----------- | -------- | ---------- | -------------- +| tag | `str` | `"div"` | HTML tag used for rendering the wrapper. + +### TabList + +| Argument | Type | Default | Description +| ----------- | -------- | ---------- | -------------- +| vertical | `bool` | `false` | Use the <kbd title="arrow up">↑</kbd> and <kbd title="arrow down">↓</kbd> arrow kbds to move between tabs instead of the defaults <kbd title="arrow left">←</kbd> and <kbd title="arrow right">→</kbd> arrow kbds. +| manual | `bool` | `false` | If `true`, selecting a tab with the keyboard won't activate it, you must press <kbd>Enter</kbd> os <kbd>Space</kbd> kbds to do it. +| tag | `str` | `"nav"` | HTML tag used for rendering the wrapper. + + +### Tab + +| Argument | Type | Default | Description +| ----------- | -------- | ---------- | -------------- +| target | `str` | | Required. HTML id of the panel associated with this tab. +| selected | `bool` | `false` | Initially selected tab. Only one tab in the `TabList` can be selected at the time. +| disabled | `bool` | `false` | If the tab can be selected. +| tag | `str` | `"button"` | HTML tag used for rendering the tab. + +### TabPanel + +| Argument | Type | Default | Description +| ----------- | -------- | ---------- | -------------- +| hidden | `bool` | `false` | Initially hidden panel. +| tag | `bool` | `"div"` | HTML tag used for rendering the panel. + + +### TabSelect + +No arguments. + + +### TabOption + +| Argument | Type | Default | Description +| ----------- | -------- | ---------- | -------------- +| target | `str` | | Required. HTML id of the panel associated with this tab. +| disabled | `bool` | `false` | Display the option but not allow to select it. + + +## Events + +A tab emits a `jxui:tab:selected` event every time is selected. The event contains the `target` property with the tag node. + +```js +document.addEventListener("jxui:tab:selected", (event) => { + console.log(`'${event.target.textContent}' tab selected`); +}); +``` + + +## Accessibility notes + +### Mouse interaction + +Clicking a `Tab` will select that tab and display the corresponding `TabPanel`. + +### Keyboard interaction + +All interactions apply when a `Tab` component is focused. + +| Command | Description +| ------------------------------------------------------------------------------------- | ----------- +| <kbd title="arrow left">←</kbd> / <kbd title="arrow right">→</kbd> arrow kbds | Selects the previous/next non-disabled tab, cycling from last to first and vice versa. +| <kbd title="arrow up">↑</kbd> / <kbd title="arrow down">↓</kbd> arrow kbds when `vertical` is set | Selects the previous/next non-disabled tab, cycling from last to first and vice versa. +| <kbd>Enter</kbd> or <kbd>Space</kbd> when `manual` is set | Activates the selected tab +| <kbd>Home</kbd> or <kbd>PageUp</kbd> | Activates the **first** tab +| <kbd>End</kbd> or <kbd>PageDown</kbd> | Activates the **last** tab |