feat: Implemented avatar component
This commit is contained in:
@@ -35,6 +35,7 @@ function buildSidebar() {
|
|||||||
function getComponents() {
|
function getComponents() {
|
||||||
return [
|
return [
|
||||||
{ text: 'Alert', link: '/components/alert/alert.md' },
|
{ text: 'Alert', link: '/components/alert/alert.md' },
|
||||||
|
{ text: 'Avatar', link: 'components/avatar/avatar.md' },
|
||||||
{ text: 'Button', link: '/components/button/button.md' },
|
{ text: 'Button', link: '/components/button/button.md' },
|
||||||
{ text: 'Button Group', link: '/components/buttonGroup/buttonGroup.md' },
|
{ text: 'Button Group', link: '/components/buttonGroup/buttonGroup.md' },
|
||||||
{ text: 'Dropdown', link: '/components/dropdown/dropdown.md' },
|
{ text: 'Dropdown', link: '/components/dropdown/dropdown.md' },
|
||||||
@@ -44,7 +45,6 @@ function getComponents() {
|
|||||||
{ text: 'Toast', link: 'components/toast/toast.md' },
|
{ text: 'Toast', link: 'components/toast/toast.md' },
|
||||||
|
|
||||||
{ text: '- Accordion', link: 'components/accordion/accordion.md' },
|
{ text: '- Accordion', link: 'components/accordion/accordion.md' },
|
||||||
{ text: '- Avatar', link: 'components/avatar/avatar.md' },
|
|
||||||
{ text: '- Badge', link: 'components/badge/badge.md' },
|
{ text: '- Badge', link: 'components/badge/badge.md' },
|
||||||
{ text: '- Breadcrumb', link: 'components/breadcrumb/breadcrumb.md' },
|
{ text: '- Breadcrumb', link: 'components/breadcrumb/breadcrumb.md' },
|
||||||
{ text: '- Card', link: 'components/card/card.md' },
|
{ text: '- Card', link: 'components/card/card.md' },
|
||||||
|
|||||||
@@ -1,15 +1,102 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AvatarExample from './examples/AvatarExample.vue'
|
import AvatarExample from './examples/AvatarExample.vue'
|
||||||
|
import AvatarBorderedExample from './examples/AvatarBorderedExample.vue'
|
||||||
|
import AvatarDotIndicatorExample from './examples/AvatarDotIndicatorExample.vue'
|
||||||
|
import AvatarSizeExample from './examples/AvatarSizeExample.vue'
|
||||||
|
import AvatarDotIndicatorPositionExample from './examples/AvatarDotIndicatorPositionExample.vue'
|
||||||
</script>
|
</script>
|
||||||
# Avatar
|
# Avatar
|
||||||
|
Use the avatar component to show a visual representation of a user profile using an image element or SVG object based on multiple styles and sizes
|
||||||
|
|
||||||
|
## Default avatar
|
||||||
|
Use this example to create a circle and rounded avatar on an image element.
|
||||||
|
|
||||||
|
<AvatarExample />
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Avatar } from 'flowbite-vue'
|
import { Avatar } from 'flowbite-vue'
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Avatar></Avatar>
|
<div class="flex">
|
||||||
|
<Avatar status="online" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" />
|
||||||
|
<Avatar status="online" rounded img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
<AvatarExample />
|
## Bordered
|
||||||
|
Use this example to create a circle and rounded avatar on an image element.
|
||||||
|
|
||||||
|
<AvatarBorderedExample />
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
import { Avatar } from 'flowbite-vue'
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="flex">
|
||||||
|
<Avatar status="online" bordered img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" bordered rounded img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dot indicator
|
||||||
|
|
||||||
|
<AvatarDotIndicatorExample />
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
import { Avatar } from 'flowbite-vue'
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="flex">
|
||||||
|
<Avatar status="online" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="busy" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="away" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="offline" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sizes
|
||||||
|
|
||||||
|
<AvatarSizeExample />
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
import { Avatar } from 'flowbite-vue'
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Avatar size="xs" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar size="sm" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar size="md" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar size="lg" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar size="xl" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dot indicator position
|
||||||
|
|
||||||
|
<AvatarDotIndicatorPositionExample />
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
import { Avatar } from 'flowbite-vue'
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Avatar status="online" status-position="top-left" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="top-left" rounded img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="top-right" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="top-right" rounded img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="bottom-left" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="bottom-left" rounded img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="bottom-right" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="bottom-right" rounded img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vp-raw flex">
|
||||||
|
<Avatar status="online" bordered img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" bordered rounded img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Avatar } from '../../../../src/index'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vp-raw flex">
|
||||||
|
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" status="online" />
|
||||||
|
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" status="busy" />
|
||||||
|
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" status="away" />
|
||||||
|
<Avatar img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" status="online" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Avatar } from '../../../../src/index'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vp-raw flex">
|
||||||
|
<Avatar status="online" status-position="top-left" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="top-left" rounded img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="top-right" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="top-right" rounded img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="bottom-left" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="bottom-left" rounded img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="bottom-right" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" status-position="bottom-right" rounded img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Avatar } from '../../../../src/index'
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="vp-raw flex flex-col">
|
<div class="vp-raw flex">
|
||||||
<Avatar></Avatar>
|
<Avatar status="online" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar status="online" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" rounded />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|||||||
14
docs/components/avatar/examples/AvatarSizeExample.vue
Normal file
14
docs/components/avatar/examples/AvatarSizeExample.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vp-raw">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Avatar size="xs" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar size="sm" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar size="md" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar size="lg" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
<Avatar size="xl" img="https://flowbite.com/docs/images/people/profile-picture-5.jpg" class="mr-2.5" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Avatar } from '../../../../src/index'
|
||||||
|
</script>
|
||||||
@@ -1,27 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="relative">
|
||||||
<!-- <img class="w-10 h-10 rounded-full" src="/docs/images/people/profile-picture-5.jpg" alt="Rounded avatar">-->
|
<img :class="avatarClasses" :src="img" :alt="alt">
|
||||||
|
<span v-if="status" :class="avatarDotClasses" :data-pos="statusPosition"></span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, toRefs } from 'vue'
|
import { toRefs } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
|
import type { AvatarSize, AvatarStatus, AvatarStatusPosition } from './types'
|
||||||
|
import { useAvatarClasses } from '@/components/Avatar/composables/useAvatarClasses'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
alt: {
|
alt: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: 'Avatar',
|
||||||
},
|
},
|
||||||
bordered: {
|
bordered: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
children: {
|
|
||||||
type: Array,
|
|
||||||
default() {
|
|
||||||
return []
|
|
||||||
},
|
|
||||||
},
|
|
||||||
img: {
|
img: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
@@ -31,7 +28,7 @@ const props = defineProps({
|
|||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String, // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
type: String as PropType<AvatarSize>,
|
||||||
default: 'md',
|
default: 'md',
|
||||||
},
|
},
|
||||||
stacked: {
|
stacked: {
|
||||||
@@ -39,13 +36,15 @@ const props = defineProps({
|
|||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
type: String, // 'away' | 'busy' | 'offline' | 'online';
|
type: String as PropType<AvatarStatus>,
|
||||||
default: '',
|
default: null,
|
||||||
},
|
},
|
||||||
statusPosition: {
|
statusPosition: {
|
||||||
type: String, // 'bottom-left' | 'bottom-right' | 'bottom-center' | 'top-left' | 'top-center' | 'top-right' | 'center-left' | 'center' | 'center-right'
|
type: String as PropType<AvatarStatusPosition>,
|
||||||
default: 'top-left',
|
default: 'top-right',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { avatarClasses, avatarDotClasses } = useAvatarClasses(toRefs(props))
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
79
src/components/Avatar/composables/useAvatarClasses.ts
Normal file
79
src/components/Avatar/composables/useAvatarClasses.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
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 ',
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
} {
|
||||||
|
|
||||||
|
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 : '',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
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],
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// TODO: Placeholder
|
||||||
|
// TODO: Stacked avatars
|
||||||
|
// TODO: Avatar Initials
|
||||||
|
|
||||||
|
return {
|
||||||
|
avatarClasses,
|
||||||
|
avatarDotClasses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
||||||
export type AvatarStatus = 'away' | 'busy' | 'offline' | 'online'
|
export type AvatarStatus = 'away' | 'busy' | 'offline' | 'online'
|
||||||
export type AvatarStatusPosition = 'bottom-left' | 'bottom-right' | 'bottom-center' | 'top-left' | 'top-center' | 'top-right' | 'center-left' | 'center' | 'center-right'
|
export type AvatarStatusPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
|
||||||
|
export type AvatarType = 'default' | 'rounded'
|
||||||
|
export type avatarDotIndicatorPositionClasses = `${AvatarStatusPosition}-${AvatarType}`
|
||||||
|
|||||||
Reference in New Issue
Block a user