feat: add avatar component

pull/609/head
Ryan Wang 2022-09-11 01:26:26 +08:00
parent e8cd837a00
commit 8e3578f445
16 changed files with 156 additions and 47 deletions

View File

@ -1,3 +1,4 @@
export * from "./components/avatar";
export * from "./components/alert";
export * from "./components/button";
export * from "./components/card";

View File

@ -0,0 +1,8 @@
<script lang="ts" setup>
import { VAvatar } from "./index";
</script>
<template>
<Story title="Avatar">
<VAvatar src="https://ryanc.cc/avatar" size="lg" />
</Story>
</template>

View File

@ -0,0 +1,89 @@
<script lang="ts" setup>
import { computed } from "vue";
import type { Size } from "./interface";
import { useImage } from "@vueuse/core";
import { IconUserLine } from "@/icons/icons";
const props = withDefaults(
defineProps<{
src?: string;
alt?: string;
size?: Size;
width?: string;
height?: string;
circle?: boolean;
}>(),
{
src: undefined,
alt: undefined,
size: undefined,
width: undefined,
height: undefined,
circle: false,
}
);
const classes = computed(() => {
const result = [`avatar-${props.circle ? "circle" : "square"}`];
if (props.size) {
result.push(`avatar-${props.size}`);
}
return result;
});
const styles = computed(() => {
const result: Record<string, string> = {};
if (props.width) {
result.width = props.width;
}
if (props.height) {
result.height = props.height;
}
return result;
});
const { isLoading, error } = useImage({ src: props.src });
</script>
<template>
<div class="avatar-wrapper" :class="classes" :style="styles">
<div v-if="isLoading || error" class="w-full h-full">
<IconUserLine class="w-full h-full" />
</div>
<img v-else :src="src" :alt="alt" />
</div>
</template>
<style lang="scss">
.avatar-wrapper {
@apply inline-flex items-center justify-center overflow-hidden bg-white;
img {
@apply w-full h-full object-cover;
}
&.avatar-circle {
@apply rounded-full;
}
&.avatar-square {
@apply rounded-base;
}
&.avatar-xs {
@apply w-6 h-6;
}
&.avatar-sm {
@apply w-8 h-8;
}
&.avatar-md {
@apply w-10 h-10;
}
&.avatar-lg {
@apply w-12 h-12;
}
}
</style>

View File

@ -0,0 +1 @@
export { default as VAvatar } from "./Avatar.vue";

View File

@ -0,0 +1 @@
export type Size = "lg" | "md" | "sm" | "xs";

View File

@ -46,6 +46,7 @@ import IconTeam from "~icons/ri/team-fill";
import IconCharacterRecognition from "~icons/ri/character-recognition-line";
import IconCalendar from "~icons/ri/calendar-line";
import IconLink from "~icons/ri/link";
import IconUserLine from "~icons/ri/user-line";
export {
IconDashboard,
@ -96,4 +97,5 @@ export {
IconCharacterRecognition,
IconCalendar,
IconLink,
IconUserLine,
};

View File

@ -7,6 +7,7 @@ import {
VModal,
VRoutesMenu,
VTag,
VAvatar,
} from "@halo-dev/components";
import type { MenuGroupType, MenuItemType } from "../types/menus";
import type { User } from "@halo-dev/api-client";
@ -59,7 +60,12 @@ const currentRole = computed(() => {
<VRoutesMenu :menus="menus" />
<div class="current-profile">
<div v-if="currentUser?.spec.avatar" class="profile-avatar">
<img :src="currentUser?.spec.avatar" class="h-11 w-11 rounded-full" />
<VAvatar
:src="currentUser?.spec.avatar"
:alt="currentUser?.spec.displayName"
size="md"
circle
></VAvatar>
</div>
<div class="profile-name">
<div class="flex text-sm font-medium">
@ -204,7 +210,8 @@ const currentRole = computed(() => {
.profile-avatar {
@apply self-center
flex;
flex
items-center;
}
.profile-name {

View File

@ -1,7 +1,7 @@
<script lang="ts" setup>
import type { User } from "@halo-dev/api-client";
import { useUserFetch } from "@/modules/system/users/composables/use-user";
import { IconCheckboxCircle } from "@halo-dev/components";
import { IconCheckboxCircle, VAvatar } from "@halo-dev/components";
const props = withDefaults(
defineProps<{
@ -57,12 +57,12 @@ function onDropdownShow() {
@click="handleSelect(user)"
>
<div class="flex items-center space-x-4 px-4 py-3">
<div class="flex-shrink-0">
<img
<div class="flex flex-shrink-0 items-center">
<VAvatar
:alt="user.spec.displayName"
:src="user.spec.avatar"
class="h-10 w-10 rounded"
/>
size="md"
></VAvatar>
</div>
<div class="min-w-0 flex-1">
<p class="truncate text-sm font-medium text-gray-900">

View File

@ -8,6 +8,7 @@ import {
VPageHeader,
VPagination,
VSpace,
VAvatar,
} from "@halo-dev/components";
import { ref } from "vue";
@ -117,12 +118,13 @@ const checkAll = ref(false);
</div>
<div class="flex">
<div
class="inline-flex flex-col flex-col-reverse items-end gap-4 sm:flex-row sm:items-center sm:gap-6"
class="inline-flex flex-col items-end gap-4 sm:flex-row sm:items-center sm:gap-6"
>
<img
class="hidden h-6 w-6 rounded-full ring-2 ring-white sm:inline-block"
<VAvatar
size="xs"
src="https://ryanc.cc/avatar"
/>
circle
></VAvatar>
<time class="text-sm text-gray-500" datetime="2020-01-07">
2020-01-07
</time>

View File

@ -15,6 +15,7 @@ import {
VSpace,
useDialog,
VEmpty,
VAvatar,
} from "@halo-dev/components";
import SinglePageSettingModal from "./components/SinglePageSettingModal.vue";
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
@ -402,14 +403,15 @@ const handleSelectUser = (user?: User) => {
name: 'UserDetail',
params: { name: contributor.name },
}"
class="flex items-center"
>
<img
<VAvatar
v-tooltip="contributor.displayName"
:alt="contributor.name"
size="xs"
:src="contributor.avatar"
:title="contributor.displayName"
class="hidden h-6 w-6 rounded-full ring-2 ring-white sm:inline-block"
/>
:alt="contributor.displayName"
circle
></VAvatar>
</RouterLink>
<span class="text-sm text-gray-500">
{{ finalStatus(singlePage.page) }}

View File

@ -17,6 +17,7 @@ import {
VPageHeader,
VPagination,
VSpace,
VAvatar,
} from "@halo-dev/components";
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
import PostSettingModal from "./components/PostSettingModal.vue";
@ -833,14 +834,15 @@ function handleContributorFilterItemChange(user?: User) {
name: 'UserDetail',
params: { name: contributor.name },
}"
class="flex items-center"
>
<img
<VAvatar
v-tooltip="contributor.displayName"
:alt="contributor.name"
size="xs"
:src="contributor.avatar"
:title="contributor.displayName"
class="hidden h-6 w-6 rounded-full ring-2 ring-white sm:inline-block"
/>
:alt="contributor.displayName"
circle
></VAvatar>
</RouterLink>
<span class="text-sm text-gray-500">
{{ finalStatus(post.post) }}

View File

@ -1,5 +1,5 @@
<script lang="ts" name="RecentLoginWidget" setup>
import { VCard } from "@halo-dev/components";
import { VCard, VAvatar } from "@halo-dev/components";
import { useUserFetch } from "@/modules/system/users/composables/use-user";
const { users } = useUserFetch({ fetchOnMounted: true });
@ -18,11 +18,11 @@ const { users } = useUserFetch({ fetchOnMounted: true });
class="cursor-pointer py-4 hover:bg-gray-50"
>
<div class="flex items-center space-x-4">
<div class="flex-shrink-0">
<img
<div class="flex flex-shrink-0 items-center">
<VAvatar
:alt="user.spec.displayName"
:src="user.spec.avatar"
class="h-10 w-10 rounded"
size="md"
/>
</div>
<div class="min-w-0 flex-1">

View File

@ -7,6 +7,7 @@ import {
VPageHeader,
VTabbar,
VTag,
VAvatar,
} from "@halo-dev/components";
import { useRoute } from "vue-router";
import { onMounted, ref, watch } from "vue";
@ -175,18 +176,12 @@ onMounted(() => {
<li class="block cursor-pointer hover:bg-gray-50">
<div class="flex items-center px-4 py-4">
<div class="flex min-w-0 flex-1 items-center">
<div class="flex-shrink-0">
<div class="h-12 w-12">
<div
class="overflow-hidden rounded border bg-white hover:shadow-sm"
>
<img
:alt="user.spec.displayName"
:src="user.spec.avatar"
class="h-full w-full"
/>
</div>
</div>
<div class="flex flex-shrink-0 items-center">
<VAvatar
:alt="user.spec.displayName"
:src="user.spec.avatar"
size="md"
/>
</div>
<div
class="min-w-0 flex-1 px-4 md:grid md:grid-cols-2 md:gap-4"

View File

@ -240,7 +240,7 @@ const handleDelete = async (role: Role) => {
</div>
<div class="flex">
<div
class="inline-flex flex-col flex-col-reverse items-end gap-4 sm:flex-row sm:items-center sm:gap-6"
class="inline-flex flex-col items-end gap-4 sm:flex-row sm:items-center sm:gap-6"
>
<FloatingTooltip
v-if="role.metadata.deletionTimestamp"

View File

@ -267,7 +267,7 @@ onMounted(() => {
</div>
<div class="flex">
<div
class="inline-flex flex-col flex-col-reverse items-end gap-4 sm:flex-row sm:items-center sm:gap-6"
class="inline-flex flex-col items-end gap-4 sm:flex-row sm:items-center sm:gap-6"
>
<div class="flex flex-col gap-1">
<time class="text-xs text-gray-500" datetime="2020-01-07">

View File

@ -11,6 +11,7 @@ import {
VPagination,
VSpace,
VTag,
VAvatar,
} from "@halo-dev/components";
import UserEditingModal from "./components/UserEditingModal.vue";
import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue";
@ -293,14 +294,12 @@ onMounted(() => {
type="checkbox"
/>
</div>
<div v-if="user.spec.avatar" class="mr-4">
<div class="h-12 w-12">
<img
:alt="user.spec.displayName"
:src="user.spec.avatar"
class="h-full w-full overflow-hidden rounded border bg-white hover:shadow-sm"
/>
</div>
<div v-if="user.spec.avatar" class="mr-4 flex items-center">
<VAvatar
:alt="user.spec.displayName"
:src="user.spec.avatar"
size="md"
></VAvatar>
</div>
<div class="flex-1">
<div class="flex flex-row items-center">