feat(Divider): new component (#757)

Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
Haytham A. Salama
2023-10-05 15:43:58 +03:00
committed by Benjamin Canac
parent 0c807db005
commit eb9ce6a0dd
6 changed files with 384 additions and 2 deletions

View File

@@ -0,0 +1,116 @@
<template>
<div :class="wrapperClass" v-bind="attrs">
<div :class="borderClass" />
<div v-if="label || icon || avatar || $slots.default" :class="containerClass">
<slot>
<span v-if="label" :class="ui.label">
{{ label }}
</span>
<UIcon v-else-if="icon" :name="icon" :class="ui.icon.base" />
<UAvatar v-else-if="avatar" v-bind="{ size: ui.avatar.size, ...avatar }" :class="ui.avatar.base" />
</slot>
</div>
<div :class="borderClass" />
</div>
</template>
<script lang="ts">
import { toRef, computed, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import { useUI } from '../../composables/useUI'
import { mergeConfig } from '../../utils'
import type { Avatar, Strategy } from '../../types'
// @ts-expect-error
import appConfig from '#build/app.config'
import { divider } from '#ui/ui.config'
const config = mergeConfig<typeof divider>(appConfig.ui.strategy, appConfig.ui.divider, divider)
export default defineComponent({
components: {
UIcon,
UAvatar
},
inheritAttrs: false,
props: {
label: {
type: String,
default: null
},
icon: {
type: String,
default: null
},
avatar: {
type: Object as PropType<Avatar>,
default: null
},
orientation: {
type: String as PropType<'horizontal' | 'vertical'>,
default: 'horizontal',
validator: (value: string) => ['horizontal', 'vertical'].includes(value)
},
type: {
type: String as PropType<'solid' | 'dotted' | 'dashed'>,
default: 'solid',
validator: (value: string) => ['solid', 'dotted', 'dashed'].includes(value)
},
class: {
type: [String, Object, Array] as PropType<any>,
default: undefined
},
ui: {
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
default: undefined
}
},
setup (props) {
const { ui, attrs } = useUI('divider', toRef(props, 'ui'), config)
const isHorizontal = computed(() => props.orientation === 'horizontal' )
const wrapperClass = computed(() => {
return twMerge(twJoin(
ui.value.wrapper.base,
isHorizontal.value ? ui.value.wrapper.horizontal : ui.value.wrapper.vertical
), props.class)
})
const containerClass = computed(() => {
return twJoin(
ui.value.container.base,
isHorizontal.value ? ui.value.container.horizontal : ui.value.container.vertical
)
})
const borderClass = computed(() => {
const typeClass = ({
solid: 'border-solid',
dotted: 'border-dotted',
dashed: 'border-dashed'
})[props.type]
return twJoin(
ui.value.border.base,
isHorizontal.value ? ui.value.border.horizontal : ui.value.border.vertical,
isHorizontal.value ? ui.value.border.size.horizontal : ui.value.border.size.vertical,
typeClass
)
})
return {
// eslint-disable-next-line vue/no-dupe-keys
ui,
attrs,
wrapperClass,
containerClass,
borderClass
}
}
})
</script>

View File

@@ -721,6 +721,36 @@ export const skeleton = {
rounded: 'rounded-md'
}
export const divider = {
wrapper: {
base: 'flex items-center align-center text-center w-full',
horizontal: 'flex-row',
vertical: 'flex-col'
},
container: {
base: 'font-medium text-gray-700 dark:text-gray-200 flex',
horizontal: 'mx-3 whitespace-nowrap',
vertical: 'my-2'
},
border: {
base: 'flex border-gray-200 dark:border-gray-800',
horizontal: 'w-full',
vertical: 'h-full',
size: {
horizontal: 'border-t',
vertical: 'border-s'
}
},
icon: {
base: 'flex-shrink-0 w-5 h-5'
},
avatar: {
base: 'flex-shrink-0',
size: '2xs'
},
label: 'text-sm'
}
// Navigation
export const verticalNavigation = {