jws-frontend/src/components/ProfileBanner.vue
2024-09-19 13:31:44 +07:00

330 lines
9.1 KiB
Vue

<script setup lang="ts">
import { ref } from 'vue';
import ToggleButton from './button/ToggleButton.vue';
defineProps<{
img?: string | null;
icon?: string;
title?: string;
caption?: string;
color?: string;
bgColor?: string;
toggleTitle?: string;
fallbackImg?: string;
fallbackCover?: string;
hideFade?: boolean;
hideActive?: boolean;
noImageAction?: boolean;
active?: boolean;
readonly?: boolean;
useToggle?: boolean;
menu?: { icon: string; color: string; bgColor: string }[];
tabsList?: { name: string | number; label: string }[];
}>();
defineEmits<{
(e: 'view'): void;
(e: 'edit'): void;
(e: 'update:toggleStatus', toggleStatus: string): void;
}>();
const coverUrl = defineModel<string>('coverUrl', {
required: false,
default: '',
});
const toggleStatus = defineModel<string>('toggleStatus', {
required: false,
default: 'CREATED',
});
const currentTab = defineModel<string | number>('currentTab');
const showOverlay = ref(false);
</script>
<template>
<q-img
fit="cover"
class="cover rounded bordered relative-position"
:style="`height: ${tabsList ? '12rem' : '10rem'}`"
:src="coverUrl || fallbackCover || '/blank-cover.png'"
@error="coverUrl = ''"
>
<nav
class="full-width full-height"
:style="`background-image: linear-gradient(
90deg, ${
$q.dark.isActive
? `hsla(var(--gray-10-hsl) / ${hideFade ? '0' : '1'}) 10%,hsla(var(--gray-10-hsl) / 0) 100%`
: `rgba(255, 255, 255, ${hideFade ? '0' : '1'}) 10%,rgba(255, 255, 255, 0) 100%`
}
)`"
>
<!-- profile -->
<div class="flex items-center full-height q-pl-lg" style="z-index: 1">
<div
class="surface-1"
style="border-radius: 50%; border: 4px solid var(--surface-1)"
>
<q-avatar
size="6rem"
font-size="3rem"
class="relative-position"
style="z-index: 1; box-shadow: var(--shadow-2)"
:style="{
color: `${color || 'white'}`,
cursor: `${noImageAction ? 'default' : 'pointer'}`,
}"
@mouseover="showOverlay = true"
@mouseleave="showOverlay = false"
@click.stop="$emit('view')"
>
<div
v-if="img"
class="full-width full-height"
:style="{
background: `${bgColor || 'var(--brand-1)'}`,
color: `${color || 'white'}`,
}"
>
<q-img id="profile-view" :src="img" :ratio="1">
<template #error>
<q-img
v-if="fallbackImg"
:src="fallbackImg"
:ratio="1"
style="background-color: transparent"
>
<template #error>
<div
class="full-width full-height flex items-center justify-center"
:style="{
background: `${bgColor || 'var(--brand-1)'}`,
color: `${color || 'white'}`,
}"
>
<q-icon :name="icon || 'mdi-account'" />
</div>
</template>
</q-img>
<div
v-else
class="full-width full-height flex items-center justify-center"
:style="{
background: `${bgColor || 'var(--brand-1)'}`,
color: `${color || 'white'}`,
}"
>
<q-icon :name="icon || 'mdi-account'" />
</div>
</template>
</q-img>
</div>
<div
v-else
class="full-width full-height flex items-center justify-center"
:style="{
background: `${bgColor || 'var(--brand-1)'}`,
color: `${color || 'white'}`,
}"
>
<q-icon :name="icon || 'mdi-account'" />
</div>
<q-badge
v-if="!hideActive"
class="absolute-bottom-right"
style="
border-radius: 10px;
width: 20px;
height: 20px;
z-index: 2;
background: var(--surface-1);
"
>
<q-badge
class="absolute-center"
style="border-radius: 7px; width: 14px; height: 14px"
:style="`background: hsl(var(${active ? '--positive-bg' : '--text-mute'}))`"
></q-badge>
</q-badge>
<Transition name="slide-fade">
<div
v-if="showOverlay && !readonly"
class="absolute text-caption full-width full-height"
style="border-radius: 50% 50%; overflow: hidden"
:class="{ dark: $q.dark.isActive }"
>
<div
class="upload-overlay absolute-bottom flex items-center justify-center"
@click.stop="$emit('edit')"
>
{{ $t('general.editImage') }}
</div>
</div>
</Transition>
</q-avatar>
</div>
</div>
<!-- text -->
<div
class="absolute-bottom relative-position column text-cover justify-between no-wrap"
:class="{ dark: $q.dark.isActive }"
>
<div
class="row justify-between full-height"
style="padding-left: 150px"
>
<div class="col column">
<span
class="text-bold ellipsis q-pt-xs text-body1"
style="width: 90%"
>
{{ title }}
<q-tooltip anchor="bottom left" self="center left" :delay="300">
{{ title }}
</q-tooltip>
</span>
<span v-if="title" class="app-text-muted">{{ caption }}</span>
</div>
<!-- icon -->
<div
v-if="$q.screen.gt.xs"
class="row items-center q-pr-lg"
style="z-index: 1"
>
<span
v-if="useToggle && toggleTitle"
class="q-mr-md app-text-muted-2"
>
{{ toggleTitle }}
</span>
<ToggleButton
v-if="useToggle"
two-way
:model-value="toggleStatus !== 'INACTIVE'"
@click="$emit('update:toggleStatus', toggleStatus)"
/>
<q-separator
v-if="useToggle"
vertical
class="q-my-lg"
spaced="lg"
style="background: hsl(var(--text-mute))"
/>
<div class="q-gutter-x-sm">
<q-btn
v-for="(item, n) in menu"
:key="n"
flat
size="sm"
class="q-pa-xs rounded"
:icon="item.icon"
:style="`background-color: ${item.bgColor}; color: ${item.color}`"
/>
</div>
</div>
</div>
<div class="row" v-if="tabsList && currentTab">
<div class="row q-px-sm full-width">
<q-tabs
dense
inline-label
mobile-arrows
v-model="currentTab"
active-class="active-tab text-weight-bold"
class="app-text-muted full-width"
align="left"
>
<q-tab
:id="`tab-${tab.label}`"
v-for="tab in tabsList"
v-bind:key="tab.name"
class="content-tab text-capitalize"
:name="tab.name"
:label="$t(tab.label)"
/>
</q-tabs>
</div>
</div>
</div>
</nav>
</q-img>
</template>
<style lang="scss">
.cover {
overflow: hidden;
background-size: cover;
background-repeat: no-repeat;
background-position: top center;
}
.text-cover {
height: 50%;
background: linear-gradient(
90deg,
rgba(255, 255, 255, 1) 35%,
rgba(255, 255, 255, 0.5) 100%
);
backdrop-filter: blur(11.2px);
-webkit-backdrop-filter: blur(11.2px);
&.dark {
background: linear-gradient(
90deg,
hsla(var(--gray-10-hsl) / 1) 35%,
hsla(var(--gray-10-hsl) / 0.3) 100%
);
}
}
.avatar__status {
content: ' ';
display: block;
block-size: 1rem;
aspect-ratio: 1;
position: absolute;
border-radius: 50%;
left: 6.5rem;
border: 1px solid var(--surface-1);
top: calc(80% - 1.2rem);
}
.upload-overlay {
top: 60%;
background-color: hsla(var(--gray-10-hsl) / 0.5);
color: white;
&.dark {
background-color: rgba(255, 255, 255, 0.2);
}
}
.slide-fade-enter-active {
transition: all 0.15s ease-out;
}
.slide-fade-leave-active {
transition: all 0.15s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter-from,
.slide-fade-leave-to {
opacity: 0;
}
.q-img__content > nav {
pointer-events: all;
}
.active-tab {
color: var(--brand-1);
}
</style>