GrTabsWithPanels

  • core
  • navigation
  • API · 9 props

Pairs a tab row with its panels under one model and one id base.

import { GrTabsWithPanels } from '@feugene/granularity/components/GrTabsWithPanels'

Overview

When to take it

  • both the tabs and the panels are needed — the pair is described by one component, without two identical values in two places;
  • the tab ↔ tabpanel link must be correct by default — idBase is generated inside, it cannot be forgotten;
  • the tabs and the panels stand next to each other — horizontally, or at the side with orientation="vertical".

When to take something else

NeedTake
Only the row of tabs, the panels are your ownGrTabs
Only the panels: the row of tabs is drawn differentlyGrTabPanels
The tabs and the panels stand in different places of the layoutthe pair GrTabs + GrTabPanels
The sections expand rather than switchGrCollapse
The view of one piece of content is switchedGrSegmented

Variants every tone and view, rendered by the real component

variant

  • pills
  • line

Sizes

  • xs
  • sm
  • md
  • lg

The grid is live: hover and press it. Hover, focus and active states are not drawn here — they belong to the browser, not to a prop.

Playground controls come from the API, not from hand

Stage

Loading…

0 changed· state is in the URL

Examples 2 each with a live demo and its code

  • A product editor in four tabs

    The row and its panels are one component: the ids and aria-controls ↔ aria-labelledby are wired inside, so there is no second value to keep in step. Row props — variant, size, tabs — pass straight through, and a tab with a problem says so with its counter. Every panel holds a form, so keep-alive keeps what was typed when the tab changes.

    Oak writing desk · draft
  • Account settings in a column

    Settings are a column: orientation="vertical" puts the row at the side and the panel beside it. The alert above refers to the security tab from outside, so the id-base is explicit — the page knows the tab’s id and moves the focus to it. The counter on «Security» goes away once two-factor sign-in is on.

    Mina Ortiz[email protected]Finance lead

API generated from the package · cannot drift

Props

PropTypedefaultDescription
variant"pills" | "line" | undefinedundefinedThe kind of row: a holder with pills or a row with an underline.
closableboolean | undefinedundefinedA cross on the tabs and closing by `Delete`/`Backspace`. Lifted pointwise with `closable: false` on the tab itself.
size"xs" | "sm" | "md" | "lg" | undefinedundefinedThe height of the tabs and the type size of the label by the controls scale. `md` by default.
orientationGrTabsOrientation | undefinedundefinedA horizontal (the default) or vertical list of tabs.
activationModeGrTabsActivationMode | undefinedundefined`automatic` (the default) — an arrow switches the tab at once; `manual` — an arrow only moves the focus, and the choice is confirmed with `Enter`/`Space`. The second mode is for tabs with heavy loading: otherwise walking through with the arrows pulls every panel.
emptyTextstring | undefinedundefinedThe text when the list of tabs is empty. The `#empty` slot is stronger.
idBasestring | undefinedundefinedThe base of the ids for the ARIA link with `GrTabPanels`. If it is set, every tab gets `id="<idBase>-tab-<value>"` and `aria-controls="<idBase>-panel-<value>"`. Pass the same `idBase` into `GrTabPanels` to link `tab`↔`tabpanel`.
modelValuerequiredv-modelstring—The `value` of the selected tab (`v-model`). If it matches none, `Tab` lands on the first available one.
tabsrequiredGrTab[]—The tabs in display order: `value`, `label`, optionally an icon, a counter and `disabled`.

Slots

SlotTypeDescription
defaultanyThe panels (`GrTabPanel`).
tab{ tab: GrTab; active: boolean; disabled: boolean; }The content of a tab as a whole — goes to `GrTabs`.
emptyanyThe empty row of tabs — goes to `GrTabs`.

Events

EventSignatureDescription
close(event: "close", value: string): void—
update:modelValue(event: "update:modelValue", value: string): void—

Types

Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.

Import
import type { GrTabsWithPanelsProps, GrTabsWithPanelsEmits } from '@feugene/granularity/components/GrTabsWithPanels'

Filedist/types/components/GrTabsWithPanels/config.d.ts

grTabsWithPanelsConfigconst
const grTabsWithPanelsConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrTabsWithPanels">;

Filedist/types/components/GrTabsWithPanels/GrTabsWithPanels.vue.d.ts

GrTabsWithPanelsPropsinterface
interface GrTabsWithPanelsProps extends GrTabsProps {
}
GrTabsWithPanelsEmitsinterface
interface GrTabsWithPanelsEmits {
    (e: 'update:modelValue', value: string): void;
    (e: 'close', value: string): void;
}

Usage from the library prose

Why it exists

The pair requires two identical values in two places — the active tab and the idBase from which the tab ↔ tabpanel link is built. Both are duplicated, and both diverge silently.

How far from theoretical that is has already been measured: GrTabs puts an id on the tabs only with an explicit idBase, so in the usual case — when it is not passed at all — the aria-labelledby of a panel pointed at a non-existent element. Here both values belong to one component, and they cannot be forgotten.

vue
<GrTabsWithPanels v-model="tab" :tabs="tabs">
  <GrTabPanel value="profile">…</GrTabPanel>
  <GrTabPanel value="billing">…</GrTabPanel>
</GrTabsWithPanels>

idBase is accepted as a prop but is not required: without it one of its own is generated. An explicit one is needed where those ids are referenced from outside.

The props of the row are passed through as they are

size, variant, activationMode, orientation, closable, emptyText and tabs go to GrTabs; the #tab and #empty slots go there too, and #default — to the panels. The close event bubbles outward.

A passed-through prop that was not set arrives in GrTabs absent rather than with the value of the wrapper. For size and variant that is a matter of principle: their default lives in the resolver of GrConfigProvider, and a substituted value would override the provider — the panel would stop listening to the global setting.

The surface of the props is held by a gate: it must match GrTabs minus what the wrapper owns itself. Otherwise the row would gain a prop the wrapper knows nothing about — the very silent divergence the component was made for.

The layout

By default the row is on top, the panels under it. orientation="vertical" puts the row at the side: vertical tabs above the panels would look like a column of links with no connection to the content.