import { Story, Meta, html, Preview } from '@open-wc/demoing-storybook'; import { LitElement } from '@lion/core'; import '../lion-tabs.js'; # Tabs `lion-tabs` implements tabs view to allow users to quickly move between a small number of equally important views. {html`

Info page with lots of information about us.

Work page that showcases our work.

`}
## How to use ### Installation ```sh npm i --save @lion/tabs; ``` ### Usage ```js import '@lion/tabs/lion-tabs.js'; ``` ```html

Info page with lots of information about us.

Work page that showcases our work.

``` ## Examples ### Selected Index You can set the `selectedIndex` to select a certain tab. {html`

Info page with lots of information about us.

Work page that showcases our work.

`}
### Slots Order The tab and panel slots are ordered by DOM order. This means you can switch the grouping in your `lion-tabs` from tab + panel to all tabs first or all panels first. {html`

Info page with lots of information about us.

Work page that showcases our work.

`}
### Distribute New Elements Below, we demonstrate on how you could dynamically add new tab + panels. {() => { const tagName = 'lion-tabs-experimental'; if (!customElements.get(tagName)) { customElements.define( tagName, class extends LitElement { static get properties() { return { __collection: { type: Array }, }; } render() { return html`

Append

panel 1

panel 2


Push

panel 1

panel 2

${this.__collection.map( item => html`

${item.panel}

`, )}
`; } constructor() { super(); this.__collection = []; } __handleAppendClick() { const tabsElement = this.shadowRoot.querySelector('#appendTabs'); const c = 2; const n = Math.floor(tabsElement.children.length / 2); for (let i = n + 1; i < n + c; i += 1) { const tab = document.createElement('button'); tab.setAttribute('slot', 'tab'); tab.innerText = `tab ${i}`; const panel = document.createElement('p'); panel.setAttribute('slot', 'panel'); panel.innerText = `panel ${i}`; tabsElement.append(tab); tabsElement.append(panel); } } __handlePushClick() { const tabsElement = this.shadowRoot.querySelector('#pushTabs'); const i = Math.floor(tabsElement.children.length / 2) + 1; this.__collection = [ ...this.__collection, { button: `tab ${i}`, panel: `panel ${i}`, }, ]; } }, ); } return html` `; }}
One way is by creating the DOM elements and appending them as needed. Inside your `lion-tabs` extension, an example for appending nodes on a certain button click: ```js __handleAppendClick() { const tabsAmount = this.children.length / 2; const tab = document.createElement('button'); tab.setAttribute('slot', 'tab'); tab.innerText = `tab ${tabsAmount + 1}`; const panel = document.createElement('p'); panel.setAttribute('slot', 'panel'); panel.innerText = `panel ${tabsAmount + 1}`; this.append(tab); this.append(panel); } ``` The other way is by adding data to a Lit property where you loop over this property in your template. You then need to ensure this causes a re-render. ```js __handlePushClick() { const tabsAmount = this.children.length; myCollection = [ ...myCollection, { button: `tab ${tabsAmount + 1}`, panel: `panel ${tabsAmount + 1}`, }, ]; renderMyCollection(); } ``` Make sure your template re-renders when myCollection is updated. ```html ${myCollection.map(item => html`

${item.panel}

`)}
``` ## Rationale ### No separate active/focus state when using keyboard We will immediately switch content as all our content comes from light dom (e.g. no latency) See Note at > It is recommended that tabs activate automatically when they receive focus as long as their > associated tab panels are displayed without noticeable latency. This typically requires tab > panel content to be preloaded. ### Panels are not focusable Focusable elements should have a means to interact with them. Tab panels themselves do not offer any interactiveness. If there is a button or a form inside the tab panel then these elements get focused directly.