feat: made all components reactive
This commit is contained in:
@@ -1,31 +1,27 @@
|
||||
<template>
|
||||
<div :class="divClasses">
|
||||
<ul :class="ulClasses">
|
||||
<li class="mr-2">
|
||||
<a href="#" aria-current="page"
|
||||
class="inline-block p-4 text-blue-600 bg-gray-100 rounded-t-lg active dark:bg-gray-800 dark:text-blue-500">Profile</a>
|
||||
</li>
|
||||
<li class="mr-2">
|
||||
<a href="#"
|
||||
class="inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300">Dashboard</a>
|
||||
</li>
|
||||
<li class="mr-2">
|
||||
<a href="#"
|
||||
class="inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300">Settings</a>
|
||||
</li>
|
||||
<li class="mr-2">
|
||||
<a href="#"
|
||||
class="inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300">Contacts</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="inline-block p-4 text-gray-400 rounded-t-lg cursor-not-allowed dark:text-gray-500">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div>
|
||||
<div :class="divClasses">
|
||||
<ul :class="ulClasses">
|
||||
<tab-pane
|
||||
v-for="(item, id) in tabsChildren"
|
||||
:key="id"
|
||||
:active="modelValueRef === item.props.name"
|
||||
:name="item.props.name"
|
||||
:disabled="item.props.disabled"
|
||||
:title="item.props.title"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
<slot/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { TAB_ACTIVATE_INJECTION_KEY, TAB_STYLE_INJECTION_KEY } from './config'
|
||||
import { useTabsClasses } from './useTabsClasses'
|
||||
import type { PropType } from 'vue'
|
||||
import { computed, provide, useSlots } from 'vue'
|
||||
import { flatten } from '../../utils/flatten'
|
||||
import TabPane from './components/TabPane/TabPane.vue'
|
||||
|
||||
export type TabsVariant = 'default' | 'underline' | 'pills'
|
||||
|
||||
@@ -34,7 +30,38 @@ const props = defineProps({
|
||||
type: String as PropType<TabsVariant>,
|
||||
default: 'default',
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const { ulClasses, divClasses } = useTabsClasses(props)
|
||||
</script>
|
||||
|
||||
provide(TAB_STYLE_INJECTION_KEY, props.variant)
|
||||
|
||||
const slots = useSlots()
|
||||
const defaultSlot = slots.default
|
||||
|
||||
const tabsChildren = computed(() => {
|
||||
return defaultSlot
|
||||
? flatten(defaultSlot()).filter((v) => {
|
||||
return (v.type as { __FLOWBITE_TAB__?: true }).__FLOWBITE_TAB__
|
||||
})
|
||||
: []
|
||||
})
|
||||
|
||||
const modelValueRef = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value: string) => emit('update:modelValue', value),
|
||||
})
|
||||
|
||||
const onActivate = (value: string) => {
|
||||
modelValueRef.value = value
|
||||
}
|
||||
|
||||
provide(TAB_ACTIVATE_INJECTION_KEY, onActivate)
|
||||
</script>
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="">
|
||||
<li class="mr-2">
|
||||
<a href="#" aria-current="page"
|
||||
class="inline-block p-4 text-blue-600 bg-gray-100 rounded-t-lg active dark:bg-gray-800 dark:text-blue-500">Profile</a>
|
||||
</li>
|
||||
<li class="mr-2">
|
||||
<a href="#"
|
||||
class="inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300">Dashboard</a>
|
||||
</li>
|
||||
<li class="mr-2">
|
||||
<a href="#"
|
||||
class="inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300">Settings</a>
|
||||
</li>
|
||||
<li class="mr-2">
|
||||
<a href="#"
|
||||
class="inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300">Contacts</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="inline-block p-4 text-gray-400 rounded-t-lg cursor-not-allowed dark:text-gray-500">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
__FLOWBITE_TAB__: true, // add this to easily find tab components from tabs
|
||||
}
|
||||
</script>
|
||||
53
src/components/Tabs/components/TabPane/TabPane.vue
Normal file
53
src/components/Tabs/components/TabPane/TabPane.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<li>
|
||||
<div :class="tabClasses" @click="tryActivateTab">
|
||||
{{ title }}
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { inject, toRefs } from 'vue'
|
||||
import { TAB_ACTIVATE_INJECTION_KEY, TAB_STYLE_INJECTION_KEY } from '../../config'
|
||||
import type { TabsVariant } from '../../Tabs.vue'
|
||||
import { useTabClasses } from './useTabClasses'
|
||||
|
||||
const props = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const variant = inject<TabsVariant>(TAB_STYLE_INJECTION_KEY)
|
||||
if(!variant) {
|
||||
console.warn('you can\'t use Tab outside of Tabs component. No tab style injection found')
|
||||
}
|
||||
|
||||
const onActivate = inject<(value: string) => void>(TAB_ACTIVATE_INJECTION_KEY)
|
||||
if(!onActivate) {
|
||||
console.warn('you can\'t use Tab outside of Tabs component. No tab activate injection found')
|
||||
}
|
||||
|
||||
const tryActivateTab = () => {
|
||||
if(props.disabled) return
|
||||
if(!onActivate) return console.warn('no onActivate')
|
||||
onActivate(props.name)
|
||||
}
|
||||
|
||||
const { tabClasses } = useTabClasses({
|
||||
...toRefs(props),
|
||||
variant,
|
||||
})
|
||||
</script>
|
||||
48
src/components/Tabs/components/TabPane/useTabClasses.ts
Normal file
48
src/components/Tabs/components/TabPane/useTabClasses.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { Ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import type { TabsVariant } from '../../Tabs.vue'
|
||||
|
||||
export type TabClassMap = { disabled: string, default: string, active: string }
|
||||
|
||||
export type UseTabClassesProps = {
|
||||
variant?: TabsVariant
|
||||
active: Ref<boolean>
|
||||
disabled: Ref<boolean>
|
||||
}
|
||||
|
||||
const defaultTabClasses: TabClassMap = {
|
||||
default: 'cursor-pointer inline-block p-4 rounded-t-lg hover:text-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800 dark:hover:text-gray-300',
|
||||
active: 'cursor-pointer inline-block p-4 text-blue-600 bg-gray-100 rounded-t-lg active dark:bg-gray-800 dark:text-blue-500',
|
||||
disabled: 'inline-block p-4 text-gray-400 rounded-t-lg cursor-not-allowed dark:text-gray-500',
|
||||
}
|
||||
const underlineTabClasses: TabClassMap = {
|
||||
default: 'cursor-pointer inline-block p-4 rounded-t-lg border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300',
|
||||
active: 'cursor-pointer inline-block p-4 text-blue-600 rounded-t-lg border-b-2 border-blue-600 active dark:text-blue-500 dark:border-blue-500',
|
||||
disabled: 'inline-block p-4 text-gray-400 rounded-t-lg cursor-not-allowed dark:text-gray-500',
|
||||
}
|
||||
const pillsTabClasses: TabClassMap = {
|
||||
default: 'cursor-pointer inline-block py-3 px-4 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800 dark:hover:text-white',
|
||||
active: 'cursor-pointer inline-block py-3 px-4 text-white bg-blue-600 rounded-lg active',
|
||||
disabled: 'inline-block py-3 px-4 text-gray-400 cursor-not-allowed dark:text-gray-500',
|
||||
}
|
||||
|
||||
export function useTabClasses(props: UseTabClassesProps): {
|
||||
tabClasses: Ref<string>,
|
||||
} {
|
||||
|
||||
const tabClasses = computed(() => {
|
||||
const tabClassType: keyof TabClassMap = props.active.value ? 'active' : props.disabled.value ? 'disabled' : 'default'
|
||||
|
||||
if(props.variant === 'default')
|
||||
return defaultTabClasses[tabClassType]
|
||||
else if(props.variant === 'underline')
|
||||
return underlineTabClasses[tabClassType]
|
||||
else if (props.variant === 'pills')
|
||||
return pillsTabClasses[tabClassType]
|
||||
return ''
|
||||
})
|
||||
|
||||
return {
|
||||
tabClasses,
|
||||
}
|
||||
}
|
||||
2
src/components/Tabs/config.ts
Normal file
2
src/components/Tabs/config.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const TAB_STYLE_INJECTION_KEY = 'flowbite-tab-style-injection'
|
||||
export const TAB_ACTIVATE_INJECTION_KEY = 'flowbite-tab-activate-func-injection'
|
||||
Reference in New Issue
Block a user