Merge branch 'main' into badge-component
# Conflicts: # docs/.vitepress/config.ts
This commit is contained in:
@@ -1,27 +1,30 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <img class="w-10 h-10 rounded-full" src="/docs/images/people/profile-picture-5.jpg" alt="Rounded avatar">-->
|
||||
<div class="relative">
|
||||
<div v-if="!img" :class="avatarPlaceholderWrapperClasses">
|
||||
<svg v-if="!initials" :class="avatarPlaceholderClasses" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<div v-else :class="avatarPlaceholderInitialsClasses">{{ initials }}</div>
|
||||
</div>
|
||||
<img v-else :class="avatarClasses" :src="img" :alt="alt">
|
||||
<span v-if="status" :class="avatarDotClasses" :data-pos="statusPosition"></span>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRefs } from 'vue'
|
||||
import { toRefs } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import type { AvatarSize, AvatarStatus, AvatarStatusPosition } from './types'
|
||||
import { useAvatarClasses } from '@/components/Avatar/composables/useAvatarClasses'
|
||||
|
||||
const props = defineProps({
|
||||
alt: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: 'Avatar',
|
||||
},
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
children: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
img: {
|
||||
type: String,
|
||||
default: '',
|
||||
@@ -31,7 +34,7 @@ const props = defineProps({
|
||||
default: false,
|
||||
},
|
||||
size: {
|
||||
type: String, // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
||||
type: String as PropType<AvatarSize>,
|
||||
default: 'md',
|
||||
},
|
||||
stacked: {
|
||||
@@ -39,13 +42,19 @@ const props = defineProps({
|
||||
default: false,
|
||||
},
|
||||
status: {
|
||||
type: String, // 'away' | 'busy' | 'offline' | 'online';
|
||||
default: '',
|
||||
type: String as PropType<AvatarStatus>,
|
||||
default: null,
|
||||
},
|
||||
statusPosition: {
|
||||
type: String, // 'bottom-left' | 'bottom-right' | 'bottom-center' | 'top-left' | 'top-center' | 'top-right' | 'center-left' | 'center' | 'center-right'
|
||||
default: 'top-left',
|
||||
type: String as PropType<AvatarStatusPosition>,
|
||||
default: 'top-right',
|
||||
},
|
||||
initials: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
|
||||
const { avatarClasses, avatarDotClasses, avatarPlaceholderClasses, avatarPlaceholderWrapperClasses, avatarPlaceholderInitialsClasses } = useAvatarClasses(toRefs(props))
|
||||
|
||||
</script>
|
||||
|
||||
5
src/components/Avatar/StackedAvatars.vue
Normal file
5
src/components/Avatar/StackedAvatars.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex -space-x-4">
|
||||
<slot name="default" />
|
||||
</div>
|
||||
</template>
|
||||
15
src/components/Avatar/StackedAvatarsCounter.vue
Normal file
15
src/components/Avatar/StackedAvatarsCounter.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<a class="relative flex justify-center items-center w-10 h-10 text-xs font-medium text-white bg-gray-700 rounded-full border-2 border-white hover:bg-gray-600 dark:border-gray-800" :href="href">+{{ total }}</a>
|
||||
</template>
|
||||
<script setup>
|
||||
defineProps({
|
||||
total: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
href: {
|
||||
type: String,
|
||||
default: '#',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
113
src/components/Avatar/composables/useAvatarClasses.ts
Normal file
113
src/components/Avatar/composables/useAvatarClasses.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { computed } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import classNames from 'classnames'
|
||||
import type { AvatarSize, AvatarStatus, AvatarStatusPosition, AvatarType, avatarDotIndicatorPositionClasses } from '@/components/Avatar/types'
|
||||
|
||||
const avatarSizeClasses: Record<AvatarSize, string> = {
|
||||
xs: 'w-6 h-6',
|
||||
sm: 'w-8 h-8',
|
||||
md: 'w-10 h-10',
|
||||
lg: 'w-20 h-20',
|
||||
xl: 'w-36 h-36',
|
||||
}
|
||||
const avatarTypeClasses: Record<AvatarType, string> = {
|
||||
default: 'rounded',
|
||||
rounded: 'rounded-full',
|
||||
}
|
||||
const avatarBorderedClasses = 'ring-2 ring-gray-300 dark:ring-gray-500 p-1'
|
||||
|
||||
const avatarStatusDotDefaultClasses = 'absolute h-3.5 w-3.5 rounded-full border-2 border-white dark:border-gray-800'
|
||||
const avatarStatusDotClasses: Record<AvatarStatus, string> = {
|
||||
away: 'bg-gray-400',
|
||||
busy: 'bg-yellow-400',
|
||||
offline: 'bg-red-400',
|
||||
online: 'bg-green-400',
|
||||
}
|
||||
const avatarStatusDotPositionClasses: Record<avatarDotIndicatorPositionClasses, string> = {
|
||||
'top-right-rounded': 'top-0 -right-0.5',
|
||||
'top-right-default': '-top-1.5 -right-1.5',
|
||||
'top-left-rounded': 'top-0 left-0',
|
||||
'top-left-default': 'top-0 left-0 transform -translate-y-1/2 -translate-x-1/2',
|
||||
'bottom-right-rounded': 'bottom-0 -right-0.5',
|
||||
'bottom-right-default': 'bottom-0 -right-1.5 translate-y-1/2',
|
||||
'bottom-left-rounded': 'bottom-0 left-0',
|
||||
'bottom-left-default': '-bottom-1.5 left-0 transform -translate-x-1/2 ',
|
||||
}
|
||||
|
||||
const avatarPlaceholderDefaultClasses = 'absolute w-auto h-auto text-gray-400'
|
||||
const avatarPlaceholderWrapperDefaultClasses = 'inline-flex overflow-hidden relative justify-center items-center bg-gray-100 dark:bg-gray-600'
|
||||
const avatarPlaceholderInitialsDefaultClasses = 'font-medium text-gray-600 dark:text-gray-300'
|
||||
const avatarPlaceholderSizes = {
|
||||
xs: 'bottom-0',
|
||||
sm: 'bottom-0',
|
||||
md: '-bottom-1',
|
||||
lg: '-bottom-2',
|
||||
xl: '-bottom-4',
|
||||
}
|
||||
|
||||
export type UseAvatarClassesProps = {
|
||||
status: Ref<AvatarStatus>
|
||||
bordered: Ref<boolean>
|
||||
img: Ref<string>
|
||||
alt: Ref<string>
|
||||
rounded: Ref<boolean>
|
||||
size: Ref<AvatarSize>
|
||||
stacked: Ref<boolean>
|
||||
statusPosition: Ref<AvatarStatusPosition>
|
||||
}
|
||||
|
||||
export function useAvatarClasses(props: UseAvatarClassesProps): {
|
||||
avatarClasses: Ref<string>
|
||||
avatarDotClasses: Ref<string>
|
||||
avatarPlaceholderClasses: Ref<string>
|
||||
avatarPlaceholderWrapperClasses: Ref<string>
|
||||
avatarPlaceholderInitialsClasses: Ref<string>
|
||||
} {
|
||||
|
||||
const avatarClasses = computed<string>(() => {
|
||||
console.log('border', props.bordered.value)
|
||||
return classNames(
|
||||
avatarSizeClasses[props.size.value],
|
||||
avatarTypeClasses[props.rounded.value ? 'rounded' : 'default'],
|
||||
props.bordered.value ? avatarBorderedClasses : '',
|
||||
props.stacked.value ? 'border-2 border-white dark:border-gray-800' : '',
|
||||
)
|
||||
})
|
||||
const avatarDotClasses = computed<string>(() => {
|
||||
const avatarType = `${props.statusPosition.value}-${props.rounded.value ? 'rounded' : 'default'}`
|
||||
return classNames(
|
||||
avatarStatusDotDefaultClasses,
|
||||
avatarStatusDotClasses[props.status.value],
|
||||
avatarStatusDotPositionClasses[avatarType as avatarDotIndicatorPositionClasses],
|
||||
)
|
||||
})
|
||||
const avatarPlaceholderClasses = computed<string>(() => {
|
||||
return classNames(
|
||||
avatarPlaceholderDefaultClasses,
|
||||
avatarPlaceholderSizes[props.size.value],
|
||||
)
|
||||
})
|
||||
const avatarPlaceholderWrapperClasses = computed<string>(() => {
|
||||
return classNames(
|
||||
avatarPlaceholderWrapperDefaultClasses,
|
||||
avatarSizeClasses[props.size.value],
|
||||
avatarTypeClasses[props.rounded.value ? 'rounded' : 'default'],
|
||||
)
|
||||
})
|
||||
const avatarPlaceholderInitialsClasses = computed<string>(() => {
|
||||
return classNames(
|
||||
avatarPlaceholderInitialsDefaultClasses,
|
||||
)
|
||||
})
|
||||
// TODO: Avatar Initials
|
||||
|
||||
return {
|
||||
avatarClasses,
|
||||
avatarDotClasses,
|
||||
avatarPlaceholderClasses,
|
||||
avatarPlaceholderWrapperClasses,
|
||||
avatarPlaceholderInitialsClasses,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5
src/components/Avatar/types.ts
Normal file
5
src/components/Avatar/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
||||
export type AvatarStatus = 'away' | 'busy' | 'offline' | 'online'
|
||||
export type AvatarStatusPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
|
||||
export type AvatarType = 'default' | 'rounded'
|
||||
export type avatarDotIndicatorPositionClasses = `${AvatarStatusPosition}-${AvatarType}`
|
||||
@@ -7,9 +7,11 @@ export { default as Tab } from './components/Tabs/components/Tab/Tab.vue'
|
||||
export { default as Dropdown } from './components/Dropdown/Dropdown.vue'
|
||||
export { default as FlowbiteThemable } from './components/utils/FlowbiteThemable/FlowbiteThemable.vue'
|
||||
export { default as FlowbiteThemableChild } from './components/utils/FlowbiteThemable/components/FlowbiteThemableChild/FlowbiteThemableChild.vue'
|
||||
|
||||
export { default as Accordion } from './components/Accordion/Accordion.vue'
|
||||
export { default as Avatar } from './components/Avatar/Avatar.vue'
|
||||
export { default as StackedAvatars } from './components/Avatar/StackedAvatars.vue'
|
||||
export { default as StackedAvatarsCounter } from './components/Avatar/StackedAvatarsCounter.vue'
|
||||
export { default as Accordion } from './components/Accordion/Accordion.vue'
|
||||
|
||||
export { default as Badge } from './components/Badge/Badge.vue'
|
||||
export { default as Breadcrumb } from './components/Breadcrumb/Breadcrumb.vue'
|
||||
export { default as Card } from './components/Card/Card.vue'
|
||||
|
||||
Reference in New Issue
Block a user