Skip to content

Commit

Permalink
feat(tabs): update tab html structure and classes (#1120)
Browse files Browse the repository at this point in the history
  • Loading branch information
mlmoravek authored Jan 31, 2025
1 parent ccae595 commit 72662d3
Show file tree
Hide file tree
Showing 14 changed files with 660 additions and 518 deletions.
1 change: 1 addition & 0 deletions packages/docs/components/Tabs.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ sidebarDepth: 2
| override | Override existing theme classes completely | boolean | - | |
| position | Position of the tabs | "centered" \| "left" \| "right" | `left`, `centered`, `right` | |
| size | Tab size | string | `small`, `medium`, `large` | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>tabs: {<br>&nbsp;&nbsp;size: undefined<br>}</code> |
| tag | Tablist tag name | DynamicComponent | - | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>tabs: {<br>&nbsp;&nbsp;tag: "div"<br>}</code> |
| type | Tab type | string | `default`, `boxed`, `toggle`, `pills` | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>tabs: {<br>&nbsp;&nbsp;type: "default"<br>}</code> |
| variant | Color of the control | string | `primary`, `info`, `success`, `warning`, `danger`, `and any other custom color` | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>tabs: {<br>&nbsp;&nbsp;variant: undefined<br>}</code> |
| vertical | Show tab in vertical layout | boolean | - | <div><small>From <b>config</b>:</small></div><code style='white-space: nowrap; padding: 0;'>tabs: {<br>&nbsp;&nbsp;vertical: false<br>}</code> |
Expand Down
42 changes: 18 additions & 24 deletions packages/oruga/src/components/tabs/TabItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ const providedData = computed<TabItemComponent<T>>(() => ({
...props,
value: itemValue,
$slots: slots,
navClasses: navItemClasses.value,
tabClasses: tabClasses.value,
iconClasses: tabIconClasses.value,
labelClasses: tabLabelClasses.value,
Expand Down Expand Up @@ -110,23 +109,6 @@ function beforeLeave(): void {
// --- Computed Component Classes ---
const navItemClasses = defineClasses(
["navItemClass", "o-tabs__nav-item"],
["navItemActiveClass", "o-tabs__nav-item--active", null, isActive],
[
"navItemPreviousClass",
"o-tabs__nav-item--previous",
null,
computed(() => item.value.index < parent.value?.activeIndex),
],
[
"navItemNextClass",
"o-tabs__nav-item--next",
null,
computed(() => item.value.index > parent.value?.activeIndex),
],
);
const tabClasses = defineClasses(
["tabClass", "o-tabs__tab"],
[
Expand All @@ -142,6 +124,18 @@ const tabClasses = defineClasses(
null,
computed(() => props.disabled),
],
[
"navItemPreviousClass",
"o-tabs__tab--previous",
null,
computed(() => item.value.index < parent.value?.activeIndex),
],
[
"navItemNextClass",
"o-tabs__tab--next",
null,
computed(() => item.value.index > parent.value?.activeIndex),
],
);
const tabIconClasses = defineClasses(["tabIconClass", "o-tabs__tab-icon"]);
Expand Down Expand Up @@ -175,12 +169,12 @@ const panelClasses = defineClasses(["tabPanelClass", "o-tabs__panel"]);
@binding {boolean} active - if item is shown
-->
<slot :active="isActive && visible">
<!-- injected component -->
<component
:is="component"
v-if="component"
v-bind="$props.props"
v-on="$props.events || {}" />
<!-- injected component -->
<component
:is="component"
v-if="component"
v-bind="$props.props"
v-on="$props.events || {}" />

<!-- default content prop -->
<template v-else>{{ content }}</template>
Expand Down
118 changes: 45 additions & 73 deletions packages/oruga/src/components/tabs/Tabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const props = withDefaults(defineProps<TabsProps<T>>(), {
vertical: () => getDefault("tabs.vertical", false),
position: undefined,
type: () => getDefault("tabs.type", "default"),
tag: () => getDefault("tabs.tag", "div"),
expanded: false,
destroyOnHide: false,
activateOnFocus: false,
Expand Down Expand Up @@ -112,7 +113,7 @@ const items = computed<TabItem<T>[]>(() => {
const { nextSequence } = useSequentialId();
/** normalized programamtic options */
const groupedOptions = computed(() =>
const normalizedOptions = computed(() =>
normalizeOptions<T>(props.options, nextSequence),
);
Expand Down Expand Up @@ -254,7 +255,19 @@ const rootClasses = defineClasses(
"positionClass",
"o-tabs--",
computed(() => props.position),
computed(() => !!props.position && props.vertical),
computed(() => !!props.position),
],
[
"sizeClass",
"o-tabs--",
computed(() => props.size),
computed(() => !!props.size),
],
[
"typeClass",
"o-tabs--",
computed(() => props.type),
computed(() => !!props.type),
],
["expandedClass", "o-tabs--expanded", null, computed(() => props.expanded)],
["verticalClass", "o-tabs--vertical", null, computed(() => props.vertical)],
Expand All @@ -266,27 +279,7 @@ const rootClasses = defineClasses(
],
);
const navClasses = defineClasses(
["navClass", "o-tabs__nav"],
[
"navSizeClass",
"o-tabs__nav--",
computed(() => props.size),
computed(() => !!props.size),
],
[
"navPositionClass",
"o-tabs__nav--",
computed(() => props.position),
computed(() => !!props.position && !props.vertical),
],
[
"navTypeClass",
"o-tabs__nav--",
computed(() => props.type),
computed(() => !!props.type),
],
);
const tablistClasses = defineClasses(["tablistClass", "o-tabs__tablist"]);
const contentClasses = defineClasses(
["contentClass", "o-tabs__content"],
Expand All @@ -301,84 +294,63 @@ const contentClasses = defineClasses(

<template>
<div ref="rootElement" :class="rootClasses" data-oruga="tabs">
<nav
:class="navClasses"
<component
:is="props.tag"
:class="tablistClasses"
role="tablist"
:aria-orientation="vertical ? 'vertical' : 'horizontal'">
<!--
@slot Additional slot before tabs
-->
<slot name="start" />

<div
<o-slot-component
v-for="childItem in items"
v-show="childItem.visible"
:id="`tab-${childItem.identifier}`"
:key="childItem.identifier"
:class="childItem.navClasses"
:component="childItem"
:tag="childItem.tag"
name="header"
:class="childItem.tabClasses"
role="tab"
:aria-controls="`tabpanel-${childItem.identifier}`"
:aria-selected="childItem.value === activeItem?.value"
:tabindex="
childItem.value === activeItem?.value ? undefined : '-1'
">
<o-slot-component
v-if="childItem.$slots.header"
:component="childItem"
:tag="childItem.tag"
name="header"
:class="childItem.tabClasses"
:props="{
active: childItem.index === activeItem?.index,
}"
@click="tabClick(childItem)"
@keydown.enter="tabClick(childItem)"
@keydown.left="prev($event, childItem.index)"
@keydown.right="next($event, childItem.index)"
@keydown.up="prev($event, childItem.index)"
@keydown.down="next($event, childItem.index)"
@keydown.home.prevent="homePressed"
@keydown.end.prevent="endPressed" />

<component
:is="childItem.tag"
v-else
role="button"
:tabindex="0"
:class="childItem.tabClasses"
@click="tabClick(childItem)"
@keydown.enter="tabClick(childItem)"
@keydown.left="prev($event, childItem.index)"
@keydown.right="next($event, childItem.index)"
@keydown.up="prev($event, childItem.index)"
@keydown.down="next($event, childItem.index)"
@keydown.home.prevent="homePressed"
@keydown.end.prevent="endPressed">
<o-icon
v-if="childItem.icon"
:class="childItem.iconClasses"
:icon="childItem.icon"
:pack="childItem.iconPack"
:size="size" />
<span :class="childItem.labelClasses">
{{ childItem.label }}
</span>
</component>
</div>
"
@click="tabClick(childItem)"
@keydown.enter="tabClick(childItem)"
@keydown.left.prevent="prev"
@keydown.right.prevent="next"
@keydown.up.prevent="prev"
@keydown.down.prevent="next"
@keydown.home.prevent="homePressed"
@keydown.end.prevent="endPressed">
<o-icon
v-if="childItem.icon"
:class="childItem.iconClasses"
:icon="childItem.icon"
:pack="childItem.iconPack"
:size="size" />
<span :class="childItem.labelClasses">
{{ childItem.label }}
</span>
</o-slot-component>

<!--
@slot Additional slot after tabs
-->
<slot name="end" />
</nav>
</component>

<section :class="contentClasses">
<!--
@slot Place tab items here
-->
<slot>
<o-tab-item
v-for="option in groupedOptions"
v-for="option in normalizedOptions"
v-show="!option.hidden"
v-bind="option.attrs"
:key="option.key"
Expand Down
47 changes: 14 additions & 33 deletions packages/oruga/src/components/tabs/examples/inspector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,16 @@ const inspectData = [
},
{
class: "positionClass",
description:
"Class of Tabs component when when is vertical and its position changes",
properties: ["position", "vertical"],
description: "Class of Tabs component position",
properties: ["position"],
suffixes: ["bottom", "left", "right"],
action: (cmp, data) => {
data.vertical = true;
data.position = "right";
},
},
{
class: "navClass",
description: "Class of the Tabs navigation",
},
{
class: "navSizeClass",
class: "sizeClass",
description: "Size of the navigation",
properties: ["size"],
suffixes: ["small", "medium", "large"],
Expand All @@ -59,36 +54,14 @@ const inspectData = [
},
},
{
class: "navTypeClass",
class: "typeClass",
description: "Type of the navigation",
properties: ["type"],
suffixes: ["default", "boxed", "toggle", "pills"],
},
{
class: "navPositionClass",
description: "Class of the Tabs component nav position",
properties: ["position"],
suffixes: ["bottom", "left", "right"],
action: (cmp, data) => {
data.vertical = false;
data.position = "right";
},
},
{
class: "navItemClass",
description: "Class of the navigation item",
},
{
class: "navItemActiveClass",
description: "Class of the nav item when active",
},
{
class: "navItemPreviousClass",
description: "Class of the nav item before the active one",
},
{
class: "navItemNextClass",
description: "Class of the nav item after the active one",
class: "tablistClass",
description: "Class of the tablist element",
},
{
class: "tabClass",
Expand All @@ -103,6 +76,14 @@ const inspectData = [
data.active = true;
},
},
{
class: "tabPreviousClass",
description: "Class of the nav item before the active one",
},
{
class: "tabNextClass",
description: "Class of the nav item after the active one",
},
{
class: "tabDisabledClass",
description: "Class of the tab item when disabled",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<section>
<o-tabs type="boxed">
<o-tabs type="boxed" multiline>
<o-tab-item
v-for="(item, index) in new Array(45)"
:key="`longitem-${index}`">
Expand Down
Loading

0 comments on commit 72662d3

Please sign in to comment.