mirror of
https://github.com/HangarMC/Hangar.git
synced 2025-01-24 14:24:47 +08:00
Add scafford for project index + info component
This commit is contained in:
parent
3d324eea81
commit
9cffe0f5d6
27
frontend-new/src/components/design/DropdownButton.vue
Normal file
27
frontend-new/src/components/design/DropdownButton.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { Menu, MenuButton, MenuItems } from "@headlessui/vue";
|
||||
import IconMdiMenuDown from "~icons/mdi/menu-down";
|
||||
import IconMdiMenuUp from "~icons/mdi/menu-up";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
name?: string;
|
||||
}>(),
|
||||
{
|
||||
name: "Dropdown",
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Menu v-slot="{ open }">
|
||||
<MenuButton class="px-4 py-2 rounded bg-primary-100 text-white font-semibold inline-flex items-center">
|
||||
<span class="mx-1">{{ props.name }}</span>
|
||||
<IconMdiMenuUp v-if="open" class="text-lg"></IconMdiMenuUp>
|
||||
<IconMdiMenuDown v-else class="text-lg"></IconMdiMenuDown>
|
||||
</MenuButton>
|
||||
<MenuItems class="absolute flex flex-col mt-1 z-10 py-1 rounded border-t-2 border-primary-100 bg-background-light-0 dark:bg-background-dark-80 shadow-soft">
|
||||
<slot></slot>
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</template>
|
46
frontend-new/src/components/design/DropdownItem.vue
Normal file
46
frontend-new/src/components/design/DropdownItem.vue
Normal file
@ -0,0 +1,46 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { MenuItem } from "@headlessui/vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
to?: string;
|
||||
href?: string;
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{
|
||||
to: undefined,
|
||||
href: undefined,
|
||||
disabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const type = computed(() => {
|
||||
if (props.disabled) {
|
||||
return "p";
|
||||
}
|
||||
if (props.to) {
|
||||
return "router-link";
|
||||
} else if (props.href) {
|
||||
return "a";
|
||||
} else {
|
||||
return "p";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MenuItem>
|
||||
<component
|
||||
:is="type"
|
||||
:class="
|
||||
'px-4 py-2 font-semibold hover:(bg-background-light-10 dark:bg-background-dark-90) ' +
|
||||
(disabled ? 'cursor-not-allowed text-opacity-50' : 'cursor-pointer')
|
||||
"
|
||||
:href="href"
|
||||
:to="to"
|
||||
>
|
||||
<slot></slot>
|
||||
</component>
|
||||
</MenuItem>
|
||||
</template>
|
81
frontend-new/src/components/projects/ProjectInfo.vue
Normal file
81
frontend-new/src/components/projects/ProjectInfo.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, PropType } from "vue";
|
||||
import { Project } from "hangar-api";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { forumUrl } from "~/composables/useUrlHelper";
|
||||
import Card from "~/components/design/Card.vue";
|
||||
import Link from "~/components/design/Link.vue";
|
||||
import DropdownButton from "~/components/design/DropdownButton.vue";
|
||||
import DropdownItem from "~/components/design/DropdownItem.vue";
|
||||
import { hasPerms } from "~/composables/usePerm";
|
||||
import { NamedPermission } from "~/types/enums";
|
||||
|
||||
const props = defineProps({
|
||||
project: {
|
||||
type: Object as PropType<Project>,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
const i18n = useI18n();
|
||||
const slug = computed(() => props.project.namespace.owner + "/" + props.project.name);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card>
|
||||
<template #header>{{ i18n.t("project.info.title") }}</template>
|
||||
<template #default>
|
||||
<table class="w-full">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-left">{{ i18n.t("project.category.info") }}</th>
|
||||
<td class="text-right">{{ i18n.t("project.category." + project.category) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-left">{{ i18n.t("project.info.publishDate") }}</th>
|
||||
<td class="text-right">{{ i18n.d(project.createdAt) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-left">{{ i18n.t("project.info.views", 0) }}</th>
|
||||
<td class="text-right">{{ project.stats.views }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-left">{{ i18n.t("project.info.totalDownloads", 0) }}</th>
|
||||
<td class="text-right">{{ project.stats.downloads }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-left">
|
||||
<Link :to="`/${slug}/stars`">
|
||||
{{ i18n.t("project.info.stars", 0) }}
|
||||
</Link>
|
||||
</th>
|
||||
<td class="text-right">{{ project.stats.stars }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-left">
|
||||
<Link :to="`/${slug}/watchers`">
|
||||
{{ i18n.t("project.info.watchers", 0) }}
|
||||
</Link>
|
||||
</th>
|
||||
<td class="text-right">{{ project.stats.watchers }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
<template #footer>
|
||||
<DropdownButton v-if="hasPerms(NamedPermission.IS_STAFF)" :name="i18n.t('project.actions.adminActions')">
|
||||
<DropdownItem :to="`/${slug}/flags`">
|
||||
{{ i18n.t("project.actions.flagHistory" /* , [project.info.flagCount] */) }}
|
||||
</DropdownItem>
|
||||
<DropdownItem :to="`/${slug}/notes`">
|
||||
{{ i18n.t("project.actions.staffNotes" /* , [project.info.noteCount] */) }}
|
||||
</DropdownItem>
|
||||
<DropdownItem :to="`/admin/log/?projectFilter=/${slug}`">
|
||||
{{ i18n.t("project.actions.userActionLogs") }}
|
||||
</DropdownItem>
|
||||
<DropdownItem v-if="project.topicId" :href="forumUrl(project.topicId)">
|
||||
{{ i18n.t("project.actions.forum") }}
|
||||
</DropdownItem>
|
||||
</DropdownButton>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
@ -22,7 +22,7 @@ function childRoute(route = ""): string {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="pt-4 pb-2 flex flex-wrap space-x-4">
|
||||
<nav class="px-2 py-4 flex flex-wrap">
|
||||
<ProjectNavItem :to="childRoute()">
|
||||
{{ i18n.t("project.tabs.docs") }}
|
||||
</ProjectNavItem>
|
||||
|
@ -16,18 +16,20 @@ const selected = computed(() => {
|
||||
});
|
||||
|
||||
const clazz = computed(() => {
|
||||
return "p-2 pb-1 rounded-sm border-b-3 flex items-center " + (selected.value ? "border-[#004ee9] font-semibold" : "border-neutral-400");
|
||||
return "py-1 rounded-sm border-b-3 " + (selected.value ? "border-[#004ee9] font-semibold " : "border-neutral-200 dark:border-neutral-700");
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-link v-if="to" :to="to" :class="clazz">
|
||||
<slot></slot>
|
||||
</router-link>
|
||||
<a v-if="href" :href="props.href" :class="clazz" target="_blank">
|
||||
<span class="mx-1">
|
||||
<div class="my-2 mr-5">
|
||||
<router-link v-if="to" :to="to" :class="clazz">
|
||||
<slot></slot>
|
||||
</span>
|
||||
<IconMdiOpenInNew class="text-xs" />
|
||||
</a>
|
||||
</router-link>
|
||||
<a v-if="href" :href="props.href" :class="clazz" target="_blank">
|
||||
<span class="mx-1">
|
||||
<slot></slot>
|
||||
</span>
|
||||
<IconMdiOpenInNew class="text-xs" />
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,3 +1,7 @@
|
||||
export function projectIconUrl(owner: string, projectName: string) {
|
||||
return `/api/internal/projects/project/${owner}/${projectName}/icon`;
|
||||
}
|
||||
|
||||
export function forumUrl(topicId: number) {
|
||||
return `https://forums.papermc.io/threads/` + topicId;
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from "vue";
|
||||
import { Project, User } from "hangar-api";
|
||||
import Card from "~/components/design/Card.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import ProjectInfo from "~/components/projects/ProjectInfo.vue";
|
||||
|
||||
defineProps({
|
||||
user: {
|
||||
@ -12,11 +15,24 @@ defineProps({
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
const i18n = useI18n();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
Project child
|
||||
{{ project }}
|
||||
<div class="flex flex-col <lg:space-y-4 lg:flex-row lg:space-x-4">
|
||||
<section class="flex-grow">
|
||||
<Card>
|
||||
Project readme
|
||||
<br />
|
||||
{{ project }}
|
||||
</Card>
|
||||
</section>
|
||||
<section class="min-w-[320px] space-y-4">
|
||||
<ProjectInfo :project="project"></ProjectInfo>
|
||||
<Card>
|
||||
<template #header>Promoted versions</template>
|
||||
<template #default>Promoted versions go here</template>
|
||||
</Card>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
Loading…
Reference in New Issue
Block a user