feat: made all components reactive
This commit is contained in:
@@ -1,8 +1,24 @@
|
||||
<template>
|
||||
<div class="vp-raw">
|
||||
<tabs />
|
||||
<tabs v-model="activeTab">
|
||||
<tab name="first" title="Проверка">
|
||||
Проверка
|
||||
</tab>
|
||||
<tab name="second" title="Проверка1">
|
||||
Проверка 2
|
||||
</tab>
|
||||
<tab name="third" title="Проверка2">
|
||||
Проверка 3
|
||||
</tab>
|
||||
<tab name="fourth" title="Проверка3" :disabled="true">
|
||||
Проверка 4
|
||||
</tab>
|
||||
</tabs>
|
||||
current tab: {{ activeTab }}
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Tabs } from '../../../../src/index'
|
||||
import { ref } from 'vue'
|
||||
import { Tabs, Tab } from '../../../../src/index'
|
||||
const activeTab = ref('')
|
||||
</script>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue'
|
||||
import { useAlertClasses } from './useAlertClasses'
|
||||
import { onBeforeUnmount, ref } from 'vue'
|
||||
import { onBeforeUnmount, ref, toRefs } from 'vue'
|
||||
|
||||
export type AlertType = 'info' | 'danger' | 'success' | 'warning' | 'dark'
|
||||
|
||||
@@ -59,7 +59,7 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const { alertClasses, textClasses, closeClasses, contentClasses, titleClasses } = useAlertClasses(props)
|
||||
const { alertClasses, textClasses, closeClasses, contentClasses, titleClasses } = useAlertClasses(toRefs(props))
|
||||
|
||||
const visible = ref(true)
|
||||
|
||||
|
||||
@@ -41,11 +41,11 @@ const closeButtonClasses: Record<AlertType, string> = {
|
||||
}
|
||||
|
||||
export type UseAlertClassesProps = {
|
||||
type: AlertType
|
||||
border: boolean
|
||||
icon: boolean
|
||||
inline: boolean
|
||||
title: string
|
||||
type: Ref<AlertType>
|
||||
border: Ref<boolean>
|
||||
icon: Ref<boolean>
|
||||
inline: Ref<boolean>
|
||||
title: Ref<string>
|
||||
}
|
||||
|
||||
export function useAlertClasses(props: UseAlertClassesProps): {
|
||||
@@ -59,40 +59,40 @@ export function useAlertClasses(props: UseAlertClassesProps): {
|
||||
const alertClasses = computed<string>(() => {
|
||||
return classNames(
|
||||
defaultAlertClasses,
|
||||
alertTypeClasses[props.type],
|
||||
alertTypeClasses[props.type.value],
|
||||
textClasses.value,
|
||||
props.border ? alertBorderClasses[props.type] : 'rounded-lg', // rounded only if no border
|
||||
props.inline ? 'flex' : '',
|
||||
props.border.value ? alertBorderClasses[props.type.value] : 'rounded-lg', // rounded only if no border
|
||||
props.inline.value ? 'flex' : '',
|
||||
)
|
||||
})
|
||||
|
||||
const textClasses = computed<string>(() => {
|
||||
return classNames(
|
||||
alertTextClasses[props.type],
|
||||
alertTextClasses[props.type.value],
|
||||
)
|
||||
})
|
||||
|
||||
const closeClasses = computed<string>(() => {
|
||||
return classNames(
|
||||
defaultCloseButtonClasses,
|
||||
closeButtonClasses[props.type],
|
||||
closeButtonClasses[props.type.value],
|
||||
)
|
||||
})
|
||||
|
||||
const contentClasses = computed<string>(() => {
|
||||
if(!props.inline) return classNames('mt-2 mb-4')
|
||||
if(!props.icon && !props.title) return ''
|
||||
return classNames(!props.title ? 'ml-3' : 'ml-1')
|
||||
if(!props.inline.value) return classNames('mt-2 mb-4')
|
||||
if(!props.icon.value && !props.title.value) return ''
|
||||
return classNames(!props.title.value ? 'ml-3' : 'ml-1')
|
||||
})
|
||||
|
||||
const titleClasses = computed<string>(() => {
|
||||
if(!props.icon || !props.inline) return classNames(
|
||||
if(!props.icon.value || !props.inline.value) return classNames(
|
||||
'font-medium',
|
||||
!props.inline ? 'text-lg ml-2' : '',
|
||||
!props.inline.value ? 'text-lg ml-2' : '',
|
||||
)
|
||||
return classNames(
|
||||
'font-medium ml-3',
|
||||
!props.inline ? 'text-lg' : '',
|
||||
!props.inline.value ? 'text-lg' : '',
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
</button>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { computed, toRefs } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import Spinner from '../Spinner/Spinner.vue'
|
||||
import { useButtonClasses } from './useButtonClasses'
|
||||
@@ -89,7 +89,7 @@ const isOutlineGradient = computed(() => props.outline && props.gradient)
|
||||
const loadingPrefix = computed(() => props.loading && props.loadingPosition === 'prefix')
|
||||
const loadingSuffix = computed(() => props.loading && props.loadingPosition === 'suffix')
|
||||
|
||||
const { wrapperClasses, spanClasses } = useButtonClasses(props)
|
||||
const { color: spinnerColor, size: spinnerSize } = useButtonSpinner(props)
|
||||
const { wrapperClasses, spanClasses } = useButtonClasses(toRefs(props))
|
||||
const { color: spinnerColor, size: spinnerSize } = useButtonSpinner(toRefs(props))
|
||||
|
||||
</script>
|
||||
|
||||
@@ -144,15 +144,15 @@ const buttonShadowClasses: Record<ButtonMonochromeGradient, string> = {
|
||||
}
|
||||
|
||||
export type UseButtonClassesProps = {
|
||||
pill: boolean
|
||||
disabled: boolean
|
||||
loading: boolean
|
||||
outline: boolean
|
||||
size: ButtonSize
|
||||
square: boolean
|
||||
color: ButtonVariant
|
||||
gradient: ButtonGradient | null
|
||||
shadow: ButtonMonochromeGradient | '' | null
|
||||
pill: Ref<boolean>
|
||||
disabled: Ref<boolean>
|
||||
loading: Ref<boolean>
|
||||
outline: Ref<boolean>
|
||||
size: Ref<ButtonSize>
|
||||
square: Ref<boolean>
|
||||
color: Ref<ButtonVariant>
|
||||
gradient: Ref<ButtonGradient | null>
|
||||
shadow: Ref<ButtonMonochromeGradient | '' | null>
|
||||
}
|
||||
|
||||
const simpleGradients = ['blue', 'green', 'cyan', 'teal', 'lime', 'red', 'pink', 'purple']
|
||||
@@ -162,64 +162,64 @@ export function useButtonClasses(props: UseButtonClassesProps): { wrapperClasses
|
||||
const slots = useSlots()
|
||||
|
||||
const sizeClasses = computed(() => {
|
||||
if (props.square) return buttonSquareSizeClasses[props.size]
|
||||
return buttonSizeClasses[props.size]
|
||||
if (props.square.value) return buttonSquareSizeClasses[props.size.value]
|
||||
return buttonSizeClasses[props.size.value]
|
||||
})
|
||||
|
||||
const bindClasses = computed(() => {
|
||||
const isGradient = !!props.gradient
|
||||
const isColor = !!props.color
|
||||
const isOutline = props.outline
|
||||
const isGradient = !!props.gradient.value
|
||||
const isColor = !!props.color.value
|
||||
const isOutline = props.outline.value
|
||||
|
||||
let hoverClass = ''
|
||||
let backgroundClass = ''
|
||||
|
||||
if (isGradient && isOutline) { // GRADIENT AND OUTLINE
|
||||
if (!simpleGradients.includes(props.gradient!)) {
|
||||
backgroundClass = buttonOutlineGradientClasses.default[props.gradient as unknown as keyof typeof buttonOutlineGradientClasses.default]
|
||||
if (!simpleGradients.includes(props.gradient.value!)) {
|
||||
backgroundClass = buttonOutlineGradientClasses.default[props.gradient.value as unknown as keyof typeof buttonOutlineGradientClasses.default]
|
||||
|
||||
if(!props.disabled)
|
||||
hoverClass = buttonOutlineGradientClasses.hover[props.gradient as unknown as keyof typeof buttonOutlineGradientClasses.hover]
|
||||
if(!props.disabled.value)
|
||||
hoverClass = buttonOutlineGradientClasses.hover[props.gradient.value as unknown as keyof typeof buttonOutlineGradientClasses.hover]
|
||||
} else {
|
||||
console.warn(`cannot use outline prop with "${props.gradient}" gradient`) // TODO: prettify
|
||||
console.warn(`cannot use outline prop with "${props.gradient.value}" gradient`) // TODO: prettify
|
||||
}
|
||||
|
||||
|
||||
} else if (isGradient) { // JUST GRADIENT
|
||||
backgroundClass = buttonGradientClasses.default[props.gradient!]
|
||||
backgroundClass = buttonGradientClasses.default[props.gradient.value!]
|
||||
|
||||
if(!props.disabled)
|
||||
hoverClass = buttonGradientClasses.hover[props.gradient!]
|
||||
if(!props.disabled.value)
|
||||
hoverClass = buttonGradientClasses.hover[props.gradient.value!]
|
||||
|
||||
|
||||
} else if (isColor && isOutline) { // COLOR AND OUTLINE
|
||||
if (!alternativeColors.includes(props.color)) {
|
||||
backgroundClass = buttonOutlineColorClasses.default[props.color as unknown as keyof typeof buttonOutlineColorClasses.default]
|
||||
if (!alternativeColors.includes(props.color.value)) {
|
||||
backgroundClass = buttonOutlineColorClasses.default[props.color.value as unknown as keyof typeof buttonOutlineColorClasses.default]
|
||||
|
||||
if(!props.disabled)
|
||||
hoverClass = buttonOutlineColorClasses.hover[props.color as unknown as keyof typeof buttonOutlineColorClasses.hover]
|
||||
if(!props.disabled.value)
|
||||
hoverClass = buttonOutlineColorClasses.hover[props.color.value as unknown as keyof typeof buttonOutlineColorClasses.hover]
|
||||
} else {
|
||||
console.warn(`cannot use outline prop with "${props.color}" color`) // TODO: prettify
|
||||
console.warn(`cannot use outline prop with "${props.color.value}" color`) // TODO: prettify
|
||||
}
|
||||
|
||||
|
||||
} else { // JUST COLOR
|
||||
backgroundClass = buttonColorClasses.default[props.color]
|
||||
backgroundClass = buttonColorClasses.default[props.color.value]
|
||||
|
||||
if(!props.disabled)
|
||||
hoverClass = buttonColorClasses.hover[props.color]
|
||||
if(!props.disabled.value)
|
||||
hoverClass = buttonColorClasses.hover[props.color.value]
|
||||
}
|
||||
|
||||
let shadowClass = ''
|
||||
if (props.shadow === '') {
|
||||
if (props.shadow.value === '') {
|
||||
// if shadow prop passed without value - try to find color for shadow by gradient
|
||||
if (props.gradient && simpleGradients.includes(props.gradient)) {
|
||||
shadowClass = buttonShadowClasses[props.gradient as unknown as keyof typeof buttonShadowClasses]
|
||||
if (props.gradient.value && simpleGradients.includes(props.gradient.value!)) {
|
||||
shadowClass = buttonShadowClasses[props.gradient.value as unknown as keyof typeof buttonShadowClasses]
|
||||
}
|
||||
} else if (typeof props.shadow === 'string') {
|
||||
} else if (typeof props.shadow.value === 'string') {
|
||||
// if provided color for shadow - use it
|
||||
if (simpleGradients.includes(props.shadow)) {
|
||||
shadowClass = buttonShadowClasses[props.shadow as unknown as keyof typeof buttonShadowClasses]
|
||||
if (simpleGradients.includes(props.shadow.value)) {
|
||||
shadowClass = buttonShadowClasses[props.shadow.value as unknown as keyof typeof buttonShadowClasses]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,19 +227,19 @@ export function useButtonClasses(props: UseButtonClassesProps): { wrapperClasses
|
||||
backgroundClass,
|
||||
hoverClass,
|
||||
shadowClass,
|
||||
props.pill ? '!rounded-full' : '',
|
||||
props.disabled ? 'cursor-not-allowed opacity-50' : '',
|
||||
props.pill.value ? '!rounded-full' : '',
|
||||
props.disabled.value ? 'cursor-not-allowed opacity-50' : '',
|
||||
(isGradient && isOutline) ? 'p-0.5' : sizeClasses.value,
|
||||
(slots.prefix || slots.suffix || props.loading) ? 'inline-flex items-center' : '',
|
||||
(slots.prefix || slots.suffix || props.loading.value) ? 'inline-flex items-center' : '',
|
||||
)
|
||||
})
|
||||
|
||||
const spanClasses = computed(() => {
|
||||
if (!!props.gradient && props.outline) { // ONLY FOR GRADIENT OUTLINE BUTTON
|
||||
if (!!props.gradient.value && props.outline.value) { // ONLY FOR GRADIENT OUTLINE BUTTON
|
||||
return classNames(
|
||||
'relative bg-white dark:bg-gray-900 rounded-md inline-flex items-center',
|
||||
sizeClasses.value,
|
||||
!props.disabled ? 'group-hover:bg-opacity-0 transition-all ease-in duration-75' : '',
|
||||
!props.disabled.value ? 'group-hover:bg-opacity-0 transition-all ease-in duration-75' : '',
|
||||
)
|
||||
}
|
||||
return ''
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type {ButtonGradient, ButtonSize, ButtonVariant} from './Button.vue'
|
||||
import type {SpinnerColor, SpinnerSize} from '../Spinner/Spinner.vue'
|
||||
import type {Ref} from 'vue'
|
||||
import {computed} from 'vue'
|
||||
import type { ButtonGradient, ButtonSize, ButtonVariant } from './Button.vue'
|
||||
import type { SpinnerColor, SpinnerSize } from '../Spinner/Spinner.vue'
|
||||
import type { Ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
export type UseButtonSpinnerProps = {
|
||||
outline: boolean
|
||||
size: ButtonSize
|
||||
color: ButtonVariant
|
||||
gradient: ButtonGradient | null
|
||||
outline: Ref<boolean>
|
||||
size: Ref<ButtonSize>
|
||||
color: Ref<ButtonVariant>
|
||||
gradient: Ref<ButtonGradient | null>
|
||||
}
|
||||
|
||||
export function useButtonSpinner(props: UseButtonSpinnerProps): { size: Ref<SpinnerSize>, color: Ref<SpinnerColor> } {
|
||||
@@ -15,26 +15,26 @@ export function useButtonSpinner(props: UseButtonSpinnerProps): { size: Ref<Spin
|
||||
lg: '5', md: '4', sm: '3', xl: '6', xs: '2.5',
|
||||
}
|
||||
const size = computed<SpinnerSize>(() => {
|
||||
return btnSizeSpinnerSizeMap[props.size]
|
||||
return btnSizeSpinnerSizeMap[props.size.value]
|
||||
})
|
||||
const color = computed<SpinnerColor>(() => {
|
||||
|
||||
if(!props.outline) return 'white'
|
||||
if(!props.outline.value) return 'white'
|
||||
|
||||
if(props.gradient) {
|
||||
if(props.gradient.includes('purple')) return 'purple'
|
||||
else if(props.gradient.includes('blue')) return 'blue'
|
||||
else if(props.gradient.includes('pink')) return 'pink'
|
||||
else if(props.gradient.includes('red')) return 'red'
|
||||
if(props.gradient.value) {
|
||||
if(props.gradient.value.includes('purple')) return 'purple'
|
||||
else if(props.gradient.value.includes('blue')) return 'blue'
|
||||
else if(props.gradient.value.includes('pink')) return 'pink'
|
||||
else if(props.gradient.value.includes('red')) return 'red'
|
||||
return 'white'
|
||||
}
|
||||
|
||||
if(['alternative', 'dark', 'light'].includes(props.color)) {
|
||||
if(['alternative', 'dark', 'light'].includes(props.color.value)) {
|
||||
return 'white'
|
||||
} else if(props.color === 'default') {
|
||||
} else if(props.color.value === 'default') {
|
||||
return 'blue'
|
||||
}
|
||||
return props.color as SpinnerColor
|
||||
return props.color.value as SpinnerColor
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue'
|
||||
import { useSpinnerClasses } from './useSpinnerClasses'
|
||||
import { toRefs } from 'vue'
|
||||
|
||||
export type SpinnerSize = '0' | 'px' | '0.5' | '1' | '1.5' | '2' | '2.5' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '10' | '11' | '12'
|
||||
export type SpinnerColor = 'blue' | 'gray' | 'green' | 'red' | 'yellow' | 'pink' | 'purple' | 'white'
|
||||
@@ -22,5 +23,5 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const { spinnerClasses } = useSpinnerClasses(props)
|
||||
const { spinnerClasses } = useSpinnerClasses(toRefs(props))
|
||||
</script>
|
||||
@@ -35,14 +35,14 @@ const colors: Record<SpinnerColor, string> = {
|
||||
}
|
||||
|
||||
export type UseSpinnerClassesProps = {
|
||||
size: SpinnerSize
|
||||
color: SpinnerColor
|
||||
size: Ref<SpinnerSize>
|
||||
color: Ref<SpinnerColor>
|
||||
}
|
||||
|
||||
export function useSpinnerClasses(props: UseSpinnerClassesProps): { spinnerClasses: Ref<string> } {
|
||||
|
||||
const sizeClasses = computed(() => sizes[props.size])
|
||||
const colorClasses = computed(() => colors[props.color])
|
||||
const sizeClasses = computed(() => sizes[props.size.value])
|
||||
const colorClasses = computed(() => colors[props.color.value])
|
||||
const bgColorClasses = computed(() => 'text-gray-200 dark:text-gray-600')
|
||||
const animateClasses = computed(() => 'animate-spin')
|
||||
|
||||
|
||||
@@ -1,31 +1,27 @@
|
||||
<template>
|
||||
<div>
|
||||
<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>
|
||||
<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)
|
||||
|
||||
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'
|
||||
33
src/utils/flatten.ts
Normal file
33
src/utils/flatten.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Fragment, createTextVNode, Comment } from 'vue'
|
||||
import type { VNodeChild, VNode } from 'vue'
|
||||
|
||||
// o(n) flatten
|
||||
export function flatten (
|
||||
vNodes: VNodeChild[],
|
||||
filterCommentNode = true,
|
||||
result: VNode[] = [],
|
||||
): VNode[] {
|
||||
vNodes.forEach((vNode) => {
|
||||
if (vNode === null) return
|
||||
if (typeof vNode !== 'object') {
|
||||
if (typeof vNode === 'string' || typeof vNode === 'number') {
|
||||
result.push(createTextVNode(String(vNode)))
|
||||
}
|
||||
return
|
||||
}
|
||||
if (Array.isArray(vNode)) {
|
||||
flatten(vNode, filterCommentNode, result)
|
||||
return
|
||||
}
|
||||
if (vNode.type === Fragment) {
|
||||
if (vNode.children === null) return
|
||||
if (Array.isArray(vNode.children)) {
|
||||
flatten(vNode.children, filterCommentNode, result)
|
||||
}
|
||||
// rawSlot
|
||||
} else if (vNode.type !== Comment) {
|
||||
result.push(vNode)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user