mirror of
https://github.com/YunoHost/yunohost-portal.git
synced 2024-09-03 20:06:23 +02:00
home: replace app list tiles with cards, logos and descriptions
This commit is contained in:
parent
4ece760ab6
commit
a5d83ce76b
4 changed files with 47 additions and 71 deletions
|
@ -43,6 +43,9 @@ defineProps<{
|
|||
@apply mb-4;
|
||||
}
|
||||
|
||||
.intro h1 {
|
||||
@apply text-6xl font-bold my-10 text-center;
|
||||
}
|
||||
.intro h2 {
|
||||
@apply text-5xl font-bold my-10;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
tag?: string
|
||||
text?: string
|
||||
srOnly?: boolean
|
||||
}>(),
|
||||
{
|
||||
tag: 'h1',
|
||||
text: undefined,
|
||||
srOnly: false,
|
||||
},
|
||||
|
@ -12,7 +14,8 @@ withDefaults(
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<h1
|
||||
<component
|
||||
:tag="tag"
|
||||
id="main-target"
|
||||
tabindex="-1"
|
||||
class="inline-block text-4xl font-bold"
|
||||
|
@ -21,11 +24,11 @@ withDefaults(
|
|||
<slot name="default">
|
||||
{{ text }}
|
||||
</slot>
|
||||
</h1>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
#main-target {
|
||||
/* Need some override here because of `not-sr-only` styles */
|
||||
margin-top: 2rem !important;
|
||||
margin-bottom: 0.75rem !important;
|
||||
|
|
|
@ -81,13 +81,13 @@ export const useQueryMsg = () => useState<string | null>('queryMsg', () => null)
|
|||
export interface Settings {
|
||||
domain: string
|
||||
public: boolean
|
||||
portal_logo: string
|
||||
portal_logo?: string
|
||||
portal_theme: string
|
||||
portal_title: string
|
||||
portal_title?: string
|
||||
show_other_domains_apps: 0 | 1
|
||||
portal_user_intro: string
|
||||
portal_public_intro: string
|
||||
apps: Record<string, { label: string; url: string }>
|
||||
apps: Record<string, { label: string; url: string, description?: Record<string, string>, logo?: string }>
|
||||
}
|
||||
|
||||
const useSettingsState = () => useState<Settings>('settings')
|
||||
|
|
100
pages/index.vue
100
pages/index.vue
|
@ -21,60 +21,43 @@ const intro = computed(() => {
|
|||
return isLoggedIn.value ? userIntro : isPublic ? publicIntro : null
|
||||
})
|
||||
|
||||
const apps = computed(() => {
|
||||
const appTileVariant = [
|
||||
'btn-primary',
|
||||
'btn-secondary',
|
||||
'btn-accent',
|
||||
'btn-neutral',
|
||||
'btn-info',
|
||||
'btn-success',
|
||||
'btn-warning',
|
||||
'btn-error',
|
||||
// FIXME currently using daisyui btn colors to get focus/hover styles,
|
||||
// if we want more colors we need to add btn variants based on daisyui colors.
|
||||
// 'bg-red-500'
|
||||
// 'bg-orange-500'
|
||||
// 'bg-yellow-500'
|
||||
// 'bg-lime-500'
|
||||
// 'bg-green-500'
|
||||
// 'bg-teal-500'
|
||||
// 'bg-indigo-500'
|
||||
// 'bg-primary',
|
||||
// 'bg-purple-500'
|
||||
// 'bg-rose-500'
|
||||
]
|
||||
|
||||
return Object.entries(appsData.value).map(([id, app]) => {
|
||||
return {
|
||||
...app,
|
||||
id,
|
||||
url: '//' + app.url,
|
||||
variant: appTileVariant[parseInt(app.label, 36) % appTileVariant.length],
|
||||
}
|
||||
})
|
||||
const apps = Object.entries(appsData.value).map(([id, app]) => {
|
||||
return {
|
||||
...app,
|
||||
url: '//' + app.url,
|
||||
description: app.description[locale.value] || app.description.en
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<PageTitle :text="$t('app_list')" sr-only />
|
||||
|
||||
<CustomText v-if="intro" :content="intro" />
|
||||
|
||||
<section id="apps" class="my-10">
|
||||
<div v-if="!apps.length" class="w-2/3">
|
||||
<section id="apps" class="my-16">
|
||||
<PageTitle :text="t('app_list')" tag="h2" sr-only class="mb-4" />
|
||||
|
||||
<div v-if="!apps.length">
|
||||
<em>{{ t('no_apps') }}</em>
|
||||
</div>
|
||||
|
||||
<ul v-else class="tile-container">
|
||||
<li v-for="app in apps" :key="app.id" class="tile relative">
|
||||
<a :href="app.url" class="btn" :class="app.variant">
|
||||
<span class="text-6xl font-extrabold" aria-hidden="true">
|
||||
{{ app.label.substring(0, 2) }}
|
||||
</span>
|
||||
<span class="leading-tight">{{ app.label }}</span>
|
||||
</a>
|
||||
<ul v-else class="grid md:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||
<li
|
||||
v-for="app in apps"
|
||||
:key="app.label"
|
||||
class="flex flex-auto border rounded p-4 relative hover:bg-neutral hover:text-neutral-content hover:border-neutral"
|
||||
>
|
||||
<img
|
||||
v-if="app.logo"
|
||||
aria-hidden="true"
|
||||
:src="app.logo"
|
||||
class="w-24 h-24 rounded me-4 bg-white"
|
||||
/>
|
||||
<div>
|
||||
<h4 class="text-xl font-bold">
|
||||
<a :href="app.url" class="">{{ app.label }}</a>
|
||||
</h4>
|
||||
<div v-if="app.description" v-html="app.description" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
@ -82,25 +65,12 @@ const apps = computed(() => {
|
|||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tile-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, 9rem);
|
||||
grid-auto-rows: 9rem;
|
||||
grid-gap: 1.5rem;
|
||||
}
|
||||
|
||||
.tile a {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.tile:hover a,
|
||||
.tile a:focus {
|
||||
transform: scale(1.05);
|
||||
.grid li a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in a new issue