mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 12:08:01 +00:00
feat: Add the design for the new tab component (#10261)
Co-authored-by: Pranav <pranavrajs@gmail.com>
This commit is contained in:
@@ -23,6 +23,7 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
// Story files can have static strings, it doesn't need to handle i18n always.
|
// Story files can have static strings, it doesn't need to handle i18n always.
|
||||||
'vue/no-bare-strings-in-template': 'off',
|
'vue/no-bare-strings-in-template': 'off',
|
||||||
|
'no-console': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup>
|
||||||
|
import TabBar from './TabBar.vue';
|
||||||
|
|
||||||
|
const defaultTabs = [
|
||||||
|
{ label: 'All articles', count: 24 },
|
||||||
|
{ label: 'Mine', count: 13 },
|
||||||
|
{ label: 'Draft', count: 5 },
|
||||||
|
{ label: 'Archived', count: 11 },
|
||||||
|
{ label: 'Unpublished', count: 23 },
|
||||||
|
{ label: 'Published', count: 100 },
|
||||||
|
{ label: 'Scheduled', count: 49 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const customTabs = [
|
||||||
|
{ label: 'Active', count: 10 },
|
||||||
|
{ label: 'Pending', count: 5 },
|
||||||
|
{ label: 'Completed', count: 15 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleTabChanged = tab => {
|
||||||
|
console.log('Tab changed:', tab);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Story title="Components/TabBar" :layout="{ type: 'grid', width: '920px' }">
|
||||||
|
<Variant title="Default">
|
||||||
|
<div class="p-4 bg-white dark:bg-slate-900">
|
||||||
|
<TabBar :tabs="defaultTabs" @tab-changed="handleTabChanged" />
|
||||||
|
</div>
|
||||||
|
</Variant>
|
||||||
|
|
||||||
|
<Variant title="Custom Tabs with Initial Tab">
|
||||||
|
<div class="p-4 bg-white dark:bg-slate-900">
|
||||||
|
<TabBar
|
||||||
|
:tabs="customTabs"
|
||||||
|
:initial-active-tab="1"
|
||||||
|
@tab-changed="handleTabChanged"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Variant>
|
||||||
|
|
||||||
|
<Variant title="No Counts">
|
||||||
|
<div class="p-4 bg-white dark:bg-slate-900">
|
||||||
|
<TabBar
|
||||||
|
:tabs="[{ label: 'Tab 1' }, { label: 'Tab 2' }, { label: 'Tab 3' }]"
|
||||||
|
@tab-changed="handleTabChanged"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Variant>
|
||||||
|
|
||||||
|
<Variant title="Single Tab">
|
||||||
|
<div class="p-4 bg-white dark:bg-slate-900">
|
||||||
|
<TabBar
|
||||||
|
:tabs="[{ label: 'Single Tab', count: 42 }]"
|
||||||
|
@tab-changed="handleTabChanged"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Variant>
|
||||||
|
</Story>
|
||||||
|
</template>
|
||||||
58
app/javascript/dashboard/components-next/tabbar/TabBar.vue
Normal file
58
app/javascript/dashboard/components-next/tabbar/TabBar.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
const props = defineProps({
|
||||||
|
initialActiveTab: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
tabs: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
validator: value => {
|
||||||
|
return value.every(
|
||||||
|
tab => typeof tab.label === 'string' && typeof tab.count === 'number'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['tabChanged']);
|
||||||
|
const activeTab = ref(props.initialActiveTab);
|
||||||
|
const selectTab = index => {
|
||||||
|
activeTab.value = index;
|
||||||
|
emit('tabChanged', props.tabs[index]);
|
||||||
|
};
|
||||||
|
const showDivider = index => {
|
||||||
|
return (
|
||||||
|
// Show dividers after the active tab, but not after the last tab
|
||||||
|
(index > activeTab.value && index < props.tabs.length - 1) ||
|
||||||
|
// Show dividers before the active tab, but not immediately before it and not before the first tab
|
||||||
|
(index < activeTab.value - 1 && index > -1)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex h-8 rounded-lg bg-slate-25 dark:bg-slate-800/50 w-fit">
|
||||||
|
<template v-for="(tab, index) in tabs" :key="index">
|
||||||
|
<button
|
||||||
|
class="relative px-4 truncate py-1.5 text-sm border-0 rounded-lg transition-colors duration-300 ease-in-out"
|
||||||
|
:class="[
|
||||||
|
activeTab === index
|
||||||
|
? 'text-woot-500 bg-woot-500/10 dark:bg-woot-500/10'
|
||||||
|
: 'text-slate-500 dark:text-slate-400 hover:text-woot-500 dark:hover:text-woot-400',
|
||||||
|
]"
|
||||||
|
@click="selectTab(index)"
|
||||||
|
>
|
||||||
|
{{ tab.label }} {{ tab.count ? `(${tab.count})` : '' }}
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="w-px h-3.5 rounded my-auto transition-colors duration-300 ease-in-out"
|
||||||
|
:class="
|
||||||
|
showDivider(index)
|
||||||
|
? 'bg-slate-75 dark:bg-slate-800'
|
||||||
|
: 'bg-transparent dark:bg-transparent'
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user