mirror of
https://github.com/nocodb/nocodb.git
synced 2026-05-01 05:26:53 +00:00
219 lines
5.8 KiB
Vue
219 lines
5.8 KiB
Vue
<script lang="ts" setup>
|
|
import { IconType, type UserType } from 'nocodb-sdk'
|
|
import 'emoji-mart-vue-fast/css/emoji-mart.css'
|
|
import { Icon } from '@iconify/vue'
|
|
import { type IconMapKey, isColorDark, stringToColor } from '#imports'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
size?: 'small' | 'medium' | 'base' | 'large' | 'xlarge' | 'auto'
|
|
user?: Partial<UserType> | Partial<User> | null
|
|
disabled?: boolean
|
|
iconBgColor?: string
|
|
showPlaceholderIcon?: boolean
|
|
isDeleted?: boolean
|
|
initialsLength?: 1 | 2
|
|
placeholderIcon?: IconMapKey
|
|
}>(),
|
|
{
|
|
user: () => ({}),
|
|
size: 'medium',
|
|
name: '',
|
|
email: '',
|
|
disabled: false,
|
|
iconBgColor: 'var(--nc-bg-gray-light)',
|
|
showPlaceholderIcon: false,
|
|
isDeleted: false,
|
|
initialsLength: 2,
|
|
},
|
|
)
|
|
|
|
const { size } = toRefs(props)
|
|
|
|
const { getColor } = useTheme()
|
|
|
|
const { getPossibleAttachmentSrc } = useAttachment()
|
|
|
|
const user = computed(() => {
|
|
return {
|
|
...(props.user || {}),
|
|
email: props.user?.email?.trim() || '',
|
|
display_name: props.user?.display_name?.trim() || '',
|
|
meta: props.user?.meta || null,
|
|
}
|
|
})
|
|
|
|
const userIcon = computed<{
|
|
icon: any
|
|
iconType: IconType | string
|
|
}>(() => {
|
|
if (props.isDeleted) {
|
|
return {
|
|
icon: 'ncSlash',
|
|
iconType: IconType.ICON,
|
|
}
|
|
}
|
|
|
|
if (!user.value.meta) {
|
|
return {
|
|
icon: '',
|
|
iconType: '',
|
|
}
|
|
}
|
|
|
|
const icon = parseProp(user.value.meta).icon || ''
|
|
const iconType = parseProp(user.value.meta).iconType || ''
|
|
|
|
if ((!icon || !iconType) && props.placeholderIcon) {
|
|
return {
|
|
icon: props.placeholderIcon,
|
|
iconType: IconType.ICON,
|
|
}
|
|
}
|
|
|
|
return {
|
|
icon: iconType === IconType.IMAGE && ncIsObject(icon) ? getPossibleAttachmentSrc(icon) || '' : (icon as string),
|
|
iconType,
|
|
}
|
|
})
|
|
|
|
const backgroundColor = computed(() => {
|
|
if (props.disabled || props.isDeleted) {
|
|
return '#bbbbbb'
|
|
}
|
|
|
|
// in comments we need to generate user icon from email
|
|
const color = user.value.display_name
|
|
? stringToColor(user.value.display_name)
|
|
: user.value.email
|
|
? stringToColor(user.value.email)
|
|
: '#FFFFFF'
|
|
|
|
if (userIcon.value.icon) {
|
|
switch (userIcon.value.iconType) {
|
|
case IconType.IMAGE: {
|
|
return ''
|
|
}
|
|
case IconType.EMOJI: {
|
|
return props.iconBgColor
|
|
}
|
|
case IconType.ICON: {
|
|
return props.iconBgColor
|
|
}
|
|
|
|
default: {
|
|
return color || '#FFFFFF'
|
|
}
|
|
}
|
|
}
|
|
|
|
return color || '#FFFFFF'
|
|
})
|
|
|
|
const usernameInitials = computed(() => {
|
|
if (props.disabled || props.isDeleted) {
|
|
return ''
|
|
}
|
|
|
|
return getSafeInitials(user.value.display_name?.trim() || user.value.email?.split('@')[0], props.initialsLength, true)
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="nc-user-avatar"
|
|
:class="{
|
|
'h-full min-h-5 aspect-square': size === 'auto',
|
|
'w-4 h-4': size === 'small',
|
|
'w-6 h-6': size === 'medium',
|
|
'w-8 h-8': size === 'base',
|
|
'w-20 h-20': size === 'large',
|
|
'w-26 h-26': size === 'xlarge',
|
|
}"
|
|
:style="{
|
|
backgroundColor:
|
|
showPlaceholderIcon || (userIcon.icon && userIcon.iconType === IconType.IMAGE) ? undefined : backgroundColor,
|
|
}"
|
|
>
|
|
<div v-if="showPlaceholderIcon" class="border-1 border-nc-border-gray-medium rounded-full overflow-hidden">
|
|
<img src="~assets/img/placeholder/avatar.png" class="!w-full h-full flex-none" />
|
|
</div>
|
|
<CellAttachmentPreviewImage
|
|
v-else-if="userIcon.icon && userIcon.iconType === IconType.IMAGE"
|
|
:srcs="userIcon.icon"
|
|
class="flex-none !object-contain max-h-full max-w-full !m-0"
|
|
:is-cell-preview="false"
|
|
/>
|
|
|
|
<div
|
|
v-else-if="userIcon.icon && userIcon.iconType === IconType.EMOJI"
|
|
class="flex items-center justify-center align-middle"
|
|
:class="{
|
|
'text-inherit': size === 'auto',
|
|
'text-white': isColorDark(getColor(backgroundColor)),
|
|
'text-black opacity-80': !isColorDark(getColor(backgroundColor)),
|
|
'text-tiny': size === 'small',
|
|
'text-base': size === 'medium',
|
|
'text-lg': size === 'base',
|
|
'text-4xl': size === 'large',
|
|
'text-5xl': size === 'xlarge',
|
|
}"
|
|
>
|
|
<div v-if="isUnicodeEmoji(userIcon.icon)">
|
|
{{ userIcon.icon }}
|
|
</div>
|
|
|
|
<Icon
|
|
v-else
|
|
:data-testid="`nc-icon-${userIcon.icon}`"
|
|
class="!text-inherit flex-none"
|
|
:class="{
|
|
'w-[75%] h-[75%]': size === 'auto',
|
|
'w-3 h-3': size === 'small',
|
|
'w-4 h-4': size === 'medium',
|
|
'w-5 h-5': size === 'base',
|
|
'w-12 h-12': size === 'large',
|
|
'w-14 h-14': size === 'xlarge',
|
|
}"
|
|
:icon="userIcon.icon"
|
|
></Icon>
|
|
</div>
|
|
<GeneralIcon
|
|
v-else-if="userIcon.icon && userIcon.iconType === IconType.ICON"
|
|
:icon="userIcon.icon"
|
|
class="flex-none"
|
|
:class="{
|
|
'w-[75%] h-[75%]': size === 'auto',
|
|
'text-white': isColorDark(getColor(backgroundColor)),
|
|
'text-black opacity-80': !isColorDark(getColor(backgroundColor)),
|
|
'w-3 h-3': size === 'small',
|
|
'w-4 h-4': size === 'medium',
|
|
'w-5 h-5': size === 'base',
|
|
'w-12 h-12': size === 'large',
|
|
'w-14 h-14': size === 'xlarge',
|
|
'!opacity-50': isDeleted,
|
|
}"
|
|
/>
|
|
<div
|
|
v-else-if="usernameInitials"
|
|
class="font-semibold"
|
|
:class="{
|
|
'!text-md': size === 'base',
|
|
'!text-3xl': size === 'large',
|
|
'!text-4xl': size === 'xlarge',
|
|
'text-white': isColorDark(getColor(backgroundColor)),
|
|
'text-black': !isColorDark(getColor(backgroundColor)),
|
|
}"
|
|
>
|
|
{{ usernameInitials }}
|
|
</div>
|
|
<div v-else> </div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.nc-user-avatar {
|
|
@apply flex-none rounded-full text-xs flex items-center justify-center uppercase overflow-hidden;
|
|
}
|
|
</style>
|