Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add avatar group component #6128

Merged
merged 1 commit into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions ui/console-src/modules/contents/_components/ContributorList.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { usePermission } from "@/utils/permission";
import type { Contributor } from "@halo-dev/api-client";
import { VAvatar } from "@halo-dev/components";
import { VAvatar, VAvatarGroup } from "@halo-dev/components";
import { useRouter } from "vue-router";

withDefaults(
Expand All @@ -26,14 +26,14 @@ function handleRouteToUserDetail(contributor: Contributor) {
</script>

<template>
<VAvatar
v-for="(contributor, contributorIndex) in contributors"
:key="contributorIndex"
v-tooltip="contributor.displayName"
size="xs"
:src="contributor.avatar"
:alt="contributor.displayName"
circle
@click="handleRouteToUserDetail(contributor)"
></VAvatar>
<VAvatarGroup size="xs" circle>
<VAvatar
v-for="contributor in contributors"
:key="contributor.name"
v-tooltip="contributor.displayName"
:src="contributor.avatar"
:alt="contributor.displayName"
@click="handleRouteToUserDetail(contributor)"
></VAvatar>
</VAvatarGroup>
</template>
8 changes: 8 additions & 0 deletions ui/packages/components/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable storybook/no-uninstalled-addons */
import type { StorybookConfig } from "@storybook/vue3-vite";

const config: StorybookConfig = {
Expand All @@ -15,5 +16,12 @@ const config: StorybookConfig = {
docs: {
autodocs: "tag",
},
async viteFinal(config) {
const { mergeConfig } = await import("vite");

return mergeConfig(config, {
assetsInclude: ["/sb-preview/runtime.js"],
});
},
};
export default config;
51 changes: 23 additions & 28 deletions ui/packages/components/src/components/avatar/Avatar.vue
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
<script lang="ts" setup>
import { computed, onMounted, ref, watch } from "vue";
import { computed, inject, onMounted, ref, watch } from "vue";
import { IconErrorWarning } from "../../icons/icons";
import type { Size } from "./interface";

const props = withDefaults(
defineProps<{
src?: string;
alt?: string;
size?: Size;
width?: string;
height?: string;
circle?: boolean;
}>(),
{
src: undefined,
alt: undefined,
size: "md",
width: undefined,
height: undefined,
circle: false,
}
);
import { AvatarGroupContextInjectionKey, type AvatarProps } from "./interface";

const props = withDefaults(defineProps<AvatarProps>(), {
size: "md",
circle: false,
});

const groupProps = inject(AvatarGroupContextInjectionKey);

const size = computed(() => groupProps?.size || props.size);
const circle = computed(() => groupProps?.circle || props.circle);
const width = computed(() => groupProps?.width || props.width);
const height = computed(() => groupProps?.height || props.height);

console.log(groupProps);

const isLoading = ref(false);
const error = ref(false);
Expand Down Expand Up @@ -65,20 +60,20 @@ onMounted(async () => {
});

const classes = computed(() => {
const result = [`avatar-${props.circle ? "circle" : "square"}`];
if (props.size) {
result.push(`avatar-${props.size}`);
const result = [`avatar-${circle.value ? "circle" : "square"}`];
if (size.value) {
result.push(`avatar-${size.value}`);
}
return result;
});

const styles = computed(() => {
const result: Record<string, string> = {};
if (props.width) {
result.width = props.width;
if (width.value) {
result.width = width.value;
}
if (props.height) {
result.height = props.height;
if (height.value) {
result.height = height.value;
}
return result;
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { Meta, StoryObj } from "@storybook/vue3";

import { VAvatar, VAvatarGroup } from ".";

const meta: Meta<typeof VAvatarGroup> = {
title: "AvatarGroup",
component: VAvatarGroup,
tags: ["autodocs"],
render: (args) => ({
components: { VAvatarGroup, VAvatar },
setup() {
return { args };
},
template: `<VAvatarGroup v-bind="args">
<VAvatar src="https://avatar.iran.liara.run/public?id=1" />
<VAvatar src="https://avatar.iran.liara.run/public?id=2" />
<VAvatar src="https://avatar.iran.liara.run/public?id=3" />
<VAvatar src="https://avatar.iran.liara.run/public?id=4" />
</VAvatarGroup>`,
}),
argTypes: {
size: {
control: { type: "select" },
options: ["lg", "md", "sm", "xs"],
defaultValue: "md",
},
},
};

export default meta;
type Story = StoryObj<typeof VAvatarGroup>;

export const Default: Story = {
args: {},
};

export const Circle: Story = {
args: {
circle: true,
},
};
30 changes: 30 additions & 0 deletions ui/packages/components/src/components/avatar/AvatarGroup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script lang="ts" setup>
import { provide } from "vue";
import {
AvatarGroupContextInjectionKey,
type AvatarGroupProps,
} from "./interface";

const props = withDefaults(defineProps<AvatarGroupProps>(), {
size: "md",
circle: false,
});

provide(AvatarGroupContextInjectionKey, props);
</script>

<template>
<div class="avatar-group-wrapper">
<slot />
</div>
</template>

<style lang="scss">
.avatar-group-wrapper {
@apply -space-x-2.5 inline-flex;

> * {
@apply hover:z-10 ring-2 ring-white transition-all;
}
}
</style>
1 change: 1 addition & 0 deletions ui/packages/components/src/components/avatar/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as VAvatar } from "./Avatar.vue";
export { default as VAvatarGroup } from "./AvatarGroup.vue";
16 changes: 16 additions & 0 deletions ui/packages/components/src/components/avatar/interface.ts
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@
import type { InjectionKey } from "vue";

export type Size = "lg" | "md" | "sm" | "xs";

export interface AvatarProps {
src?: string;
alt?: string;
size?: Size;
width?: string;
height?: string;
circle?: boolean;
}

export type AvatarGroupProps = Omit<AvatarProps, "src" | "alt">;

export const AvatarGroupContextInjectionKey: InjectionKey<AvatarGroupProps> =
Symbol("avatar-group-context");
19 changes: 10 additions & 9 deletions ui/uc-src/modules/contents/posts/components/PostListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IconTimerLine,
Toast,
VAvatar,
VAvatarGroup,
VDropdownDivider,
VDropdownItem,
VEntity,
Expand Down Expand Up @@ -179,15 +180,15 @@ function handleUnpublish() {
<template #end>
<VEntityField>
<template #description>
<VAvatar
v-for="{ name, avatar, displayName } in post.contributors"
:key="name"
v-tooltip="displayName"
size="xs"
:src="avatar"
:alt="displayName"
circle
></VAvatar>
<VAvatarGroup size="xs" circle>
<VAvatar
v-for="{ name, avatar, displayName } in post.contributors"
:key="name"
v-tooltip="displayName"
:src="avatar"
:alt="displayName"
></VAvatar>
</VAvatarGroup>
</template>
</VEntityField>
<VEntityField :description="publishStatus">
Expand Down