Skip to content

Commit

Permalink
feat: adds tabs component
Browse files Browse the repository at this point in the history
  • Loading branch information
gokh4nozturk authored and Gökhan Öztürk committed Sep 23, 2022
1 parent abd5900 commit 691a064
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 0 deletions.
215 changes: 215 additions & 0 deletions src/components/Tabs/Tabs.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';

import Tabs from './Tabs.vue';

export const defaultArgs = {
tabs: {
control: {
type: 'object',
},
defaultValue: [
{ label: 'Tab 1', icon: 'home', iconKind: 'filled', badge: '10' },
{ label: 'Tab 2', icon: 'add_circle', disabled: true },
{ label: 'Tab 3', icon: 'favorite' },
{ label: 'Tab 4', icon: 'face' },
{ label: 'Tab 5', icon: 'settings' },
],
},
variant: {
control: {
type: 'select',
options: [
'default',
'pill',
'underline',
'icon',
'icon-pill',
'icon-underline',
'icon-only',
],
},
defaultValue: 'default',
},
size: {
control: {
type: 'select',
options: ['small', 'medium', 'large'],
},
defaultValue: 'medium',
},
color: {
control: {
type: 'color',
},
defaultValue: '',
},
bgColor: {
control: {
type: 'color',
},
defaultValue: '',
},
activeColor: {
control: {
type: 'color',
},
defaultValue: '',
},
activeBgColor: {
control: {
type: 'color',
},
defaultValue: '',
},
align: {
control: {
type: 'select',
options: ['left', 'center', 'right'],
},
defaultValue: 'center',
},
scrollable: {
control: {
type: 'boolean',
},
defaultValue: false,
},
onClick: {
action: 'click',
},
};

<Meta title="Components/Tabs" component={Tabs} argTypes={{ ...defaultArgs }} />

export const Default = (args) => ({
components: { Tabs },
setup() {
return { args };
},
template: `
<Tabs v-bind="args">
<template #tab={activeTab}>
<h1>Tab {{activeTab + 1}}</h1>
<p>Active tab {{activeTab}}</p>
</template>
</Tabs>
`,
});

# Default

<Canvas>
<Story
name="Default"
argTypes={{ ...Default.argTypes }}
args={{ variant: 'default' }}
>
{Default.bind({})}
</Story>
</Canvas>

# Icon Only

<Canvas>
<Story
name="Icon Only"
argTypes={{ ...Default.argTypes }}
args={{
variant: 'icon-only',
tabs: [
{ label: 'Tab 1', icon: 'home', iconKind: 'filled' },
{ label: 'Tab 2', icon: 'add_circle', disabled: true },
{ label: 'Tab 3', icon: 'favorite', badge: 1 },
{ label: 'Tab 4', icon: 'face' },
{ label: 'Tab 5', icon: 'settings' },
{ label: 'Tab 6', icon: 'search' },
{ label: 'Tab 7', icon: 'done' },
{ label: 'Tab 8', icon: 'visibility' },
{ label: 'Tab 9', icon: 'lock' },
{ label: 'Tab 10', icon: 'language' },
],
}}
>
{Default.bind({})}
</Story>
</Canvas>

# Pill

<Canvas>
<Story
name="Pill"
argTypes={{ ...Default.argTypes }}
args={{
variant: 'pill',
tabs: [
{ label: 'Tab 1', icon: 'home', iconKind: 'filled' },
{ label: 'Tab 2', icon: 'add_circle', disabled: true },
{ label: 'Tab 3', icon: 'favorite' },
{ label: 'Tab 4', icon: 'face' },
{ label: 'Tab 5', icon: 'settings' },
{ label: 'Tab 6', icon: 'search' },
{ label: 'Tab 7', icon: 'done' },
{ label: 'Tab 8', icon: 'visibility' },
{ label: 'Tab 9', icon: 'lock' },
{ label: 'Tab 10', icon: 'language' },
],
}}
>
{Default.bind({})}
</Story>
</Canvas>

# Icon Pill

<Canvas>
<Story
name="Icon Pill"
argTypes={{ ...Default.argTypes }}
args={{
variant: 'icon-pill',
tabs: [
{ label: 'Tab 1', icon: 'home', iconKind: 'filled' },
{ label: 'Tab 2', icon: 'add_circle', disabled: true },
{ label: 'Tab 3', icon: 'favorite' },
{ label: 'Tab 4', icon: 'face' },
{ label: 'Tab 5', icon: 'settings' },
{ label: 'Tab 6', icon: 'search' },
{ label: 'Tab 7', icon: 'done' },
{ label: 'Tab 8', icon: 'visibility' },
{ label: 'Tab 9', icon: 'lock' },
{ label: 'Tab 10', icon: 'language' },
],
}}
>
{Default.bind({})}
</Story>
</Canvas>

# Text Only

<Canvas>
<Story
name="Text Only"
argTypes={{ ...Default.argTypes }}
args={{
variant: 'text-only',
tabs: [
{ label: 'Tab 1', icon: 'home', iconKind: 'filled' },
{ label: 'Tab 2', icon: 'add_circle', disabled: true },
{ label: 'Tab 3', icon: 'favorite' },
{ label: 'Tab 4', icon: 'face' },
{ label: 'Tab 5', icon: 'settings' },
{ label: 'Tab 6', icon: 'search' },
{ label: 'Tab 7', icon: 'done' },
{ label: 'Tab 8', icon: 'visibility' },
{ label: 'Tab 9', icon: 'lock' },
{ label: 'Tab 10', icon: 'language' },
],
}}
>
{Default.bind({})}
</Story>
</Canvas>

<ArgsTable story="Default" />
65 changes: 65 additions & 0 deletions src/components/Tabs/Tabs.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script setup lang="ts">
import { computed, ref, defineEmits } from 'vue';
import './tabs.css';
import type { Tab } from './types';
import TabItem from '../TabItem/TabItem.vue';
export interface IProps {
tabs: Tab[];
variant?: 'default' | 'pill' | 'icon-only' | 'icon-pill' | 'text-only';
size?: 'small' | 'medium' | 'large';
color?: string;
bgColor?: string;
activeColor?: string;
activeBgColor?: string;
align?: 'left' | 'center' | 'right';
scrollable?: boolean;
}
const props = withDefaults(defineProps<IProps>(), {
tabs: () => [],
variant: 'default',
color: '',
activeColor: '',
size: 'medium',
align: 'left',
scrollable: false,
activeTab: 0,
});
const tabsClasses = computed(() => {
return {
tabs: true,
[`tabs--${props.variant}`]: true,
'tabs--scrollable': props.scrollable,
};
});
const activeTab = ref(0);
const emit = defineEmits(['click']);
const onClick = (index: number) => {
activeTab.value = index;
emit('click', index);
};
</script>
<template>
<div class="tabs-wrapper">
<div :class="tabsClasses">
<TabItem
v-for="(tab, index) in props.tabs"
:key="index"
:id="index"
:tab="tab"
:color="props.color"
:activeColor="props.activeColor"
:active="activeTab === index"
:variant="props.variant"
:size="props.size"
:align="props.align"
@click="onClick"
/>
</div>
</div>
<div class="tab-content">
<slot name="tab" :activeTab="activeTab" />
</div>
</template>
25 changes: 25 additions & 0 deletions src/components/Tabs/tabs.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@tailwind components;

* {
@apply font-sans;
}

.tabs-wrapper {
@apply w-full grid place-items-center;
}

.tabs {
@apply flex w-full border-0 border-b border-gray-200 border-solid;
}

.tab-wrapper {
@apply flex flex-col w-full;
}

.tab {
@apply w-full p-2 bg-transparent border-none flex justify-center items-center text-gray-500 text-sm font-medium cursor-pointer disabled:cursor-not-allowed disabled:bg-inherit disabled:opacity-50;
}

.tabs--scrollable {
@apply overflow-x-auto;
}
7 changes: 7 additions & 0 deletions src/components/Tabs/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type Tab = {
label?: string;
icon?: string;
disabled?: boolean;
iconKind?: string;
badge?: string;
};

0 comments on commit 691a064

Please # to comment.