mirror of
https://github.com/unraid/api.git
synced 2026-01-02 14:40:01 -06:00
refactor: dropdown components
This commit is contained in:
@@ -4,31 +4,23 @@ import { ArrowRightOnRectangleIcon, ArrowTopRightOnSquareIcon, CogIcon } from '@
|
||||
|
||||
import { ACCOUNT, CONNECT_DASHBOARD, PLUGIN_SETTINGS } from '~/helpers/urls';
|
||||
import { useServerStore } from '~/store/server';
|
||||
|
||||
export interface Props {
|
||||
show?: boolean;
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
show: false,
|
||||
});
|
||||
import type { ServerStateDataAction } from '~/types/server';
|
||||
import type { UserProfileLink } from '~/types/userProfile';
|
||||
|
||||
const myServersEnv = ref<string>('Staging');
|
||||
const devEnv = ref<boolean>(true);
|
||||
const devEnv = ref<string>('development');
|
||||
|
||||
const serverStore = useServerStore();
|
||||
const { stateData } = storeToRefs(serverStore);
|
||||
|
||||
interface Link {
|
||||
emphasize?: boolean;
|
||||
external?: boolean;
|
||||
href: string;
|
||||
icon?: typeof CogIcon;
|
||||
text: string;
|
||||
title?: string;
|
||||
}
|
||||
// Intended to hide sign in and sign out from actions v-for in UPC dropdown so we can display them separately
|
||||
const stateDataKeyActions = computed((): ServerStateDataAction[] | undefined => {
|
||||
const notAllowed = ['signIn', 'signOut'];
|
||||
if (!stateData.value.actions) return;
|
||||
return stateData.value.actions.filter(action => !notAllowed.includes(action.name));
|
||||
});
|
||||
|
||||
const links = computed(():Link[] => {
|
||||
const links = computed(():UserProfileLink[] => {
|
||||
return [
|
||||
{
|
||||
emphasize: true,
|
||||
@@ -56,22 +48,19 @@ const links = computed(():Link[] => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav v-show="show" class="Dropdown absolute z-30 right-0 flex flex-col gap-y-8px p-8px bg-alpha border rounded-lg min-w-310px max-w-350px">
|
||||
<upc-dropdown-wrapper class="Dropdown min-w-300px max-w-350px">
|
||||
<header class="flex flex-row items-start justify-between mt-8px mx-8px">
|
||||
<h3 class="text-18px leading-none inline-flex flex-row gap-x-8px items-center">
|
||||
<span class="font-semibold">Connect</span>
|
||||
<upc-beta />
|
||||
<span v-if="myServersEnv" :title="`API • ${myServersEnv}`">⚙️</span>
|
||||
<span v-if="devEnv" :title="devEnv">⚠️</span>
|
||||
<span v-if="devEnv" :title="`UPC • ${devEnv}`">⚠️</span>
|
||||
</h3>
|
||||
</header>
|
||||
<ul class="list-reset flex flex-col gap-y-4px p-0">
|
||||
<template v-if="stateData.actions">
|
||||
<li v-for="action in stateData.actions" :key="action.name">
|
||||
<button @click="action.click" class="Dropdown_link">
|
||||
<component :is="action.icon" class="Dropdown_linkIcon" aria-hidden="true" />
|
||||
{{ action.text }}
|
||||
</button>
|
||||
<template v-if="stateDataKeyActions">
|
||||
<li v-for="action in stateDataKeyActions" :key="action.name">
|
||||
<upc-dropdown-item :item="action" />
|
||||
</li>
|
||||
</template>
|
||||
|
||||
@@ -81,7 +70,8 @@ const links = computed(():Link[] => {
|
||||
|
||||
<template v-if="links">
|
||||
<li v-for="(link, index) in links" :key="`link_${index}`">
|
||||
<a
|
||||
<upc-dropdown-item :item="link" />
|
||||
<!-- <a
|
||||
:href="link.href"
|
||||
:title="link.title"
|
||||
:target="link.external ? '_blank' : ''"
|
||||
@@ -93,11 +83,11 @@ const links = computed(():Link[] => {
|
||||
>
|
||||
<component :is="link.icon" class="Dropdown_linkIcon" aria-hidden="true" />
|
||||
{{ link.text }}
|
||||
</a>
|
||||
</a> -->
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</nav>
|
||||
</upc-dropdown-wrapper>
|
||||
</template>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
@@ -120,32 +110,4 @@ const links = computed(():Link[] => {
|
||||
border-left: 11px solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.Dropdown_link {
|
||||
@apply text-left text-14px text-beta;
|
||||
@apply w-full flex flex-row items-center;
|
||||
@apply gap-x-8px px-16px py-8px;
|
||||
@apply bg-transparent;
|
||||
@apply cursor-pointer rounded-md;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
@apply text-white;
|
||||
@apply bg-gradient-to-r from-red to-orange;
|
||||
@apply outline-none;
|
||||
}
|
||||
|
||||
&--emphasize {
|
||||
@apply text-white bg-gradient-to-r from-red to-orange;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
@apply from-red/60 to-orange/60;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Dropdown_linkIcon {
|
||||
@apply flex-shrink-0 fill-current w-16px h-16px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
64
components/UserProfile/DropdownItem.vue
Normal file
64
components/UserProfile/DropdownItem.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<script setup lang="ts">
|
||||
import { ArrowTopRightOnSquareIcon } from '@heroicons/vue/24/solid';
|
||||
import type { ServerStateDataAction } from '~/types/server';
|
||||
import type { UserProfileLink } from '~/types/userProfile';
|
||||
|
||||
export interface Props {
|
||||
item: ServerStateDataAction | UserProfileLink;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const showExternalIconOnHover = computed(() => props.item?.click || (props.item?.external && props.item.icon !== ArrowTopRightOnSquareIcon));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component
|
||||
:is="item?.click ? 'button' : 'a'"
|
||||
@click="item?.click ?? null"
|
||||
:href="item?.href ?? null"
|
||||
:title="item?.title ?? null"
|
||||
:target="item?.external ? '_blank' : null"
|
||||
:rel="item?.external ? 'noopener noreferrer' : null"
|
||||
class="DropdownItem"
|
||||
:class="{
|
||||
'DropdownItem--emphasize': item?.emphasize,
|
||||
'group': showExternalIconOnHover,
|
||||
}"
|
||||
>
|
||||
<span class="inline-flex flex-row items-center gap-x-8px">
|
||||
<component :is="item?.icon" class="flex-shrink-0 fill-current w-16px h-16px" aria-hidden="true" />
|
||||
{{ item?.text }}
|
||||
</span>
|
||||
<ArrowTopRightOnSquareIcon
|
||||
v-if="showExternalIconOnHover"
|
||||
class="text-white fill-current w-16px h-16px ml-8px opacity-0 group-hover:opacity-100 transition-opacity duration-200 ease-in-out"
|
||||
/>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.DropdownItem {
|
||||
@apply text-left text-14px text-beta;
|
||||
@apply w-full flex flex-row items-center justify-between;
|
||||
@apply gap-x-8px px-8px py-8px;
|
||||
@apply bg-transparent;
|
||||
@apply cursor-pointer rounded-md;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
@apply text-white;
|
||||
@apply bg-gradient-to-r from-red to-orange;
|
||||
@apply outline-none;
|
||||
}
|
||||
|
||||
&--emphasize {
|
||||
@apply text-white bg-gradient-to-r from-red to-orange;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
@apply from-red/60 to-orange/60;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
5
components/UserProfile/DropdownWrapper.vue
Normal file
5
components/UserProfile/DropdownWrapper.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<nav class="absolute z-30 right-0 flex flex-col gap-y-8px p-8px bg-alpha border rounded-lg">
|
||||
<slot/>
|
||||
</nav>
|
||||
</template>
|
||||
@@ -1,7 +1,9 @@
|
||||
import { defineStore, createPinia, setActivePinia } from "pinia";
|
||||
import { ArrowRightOnRectangleIcon, GlobeAltIcon, KeyIcon } from '@heroicons/vue/24/solid';
|
||||
|
||||
import type { Server, ServerState, ServerStateData } from '~/types/server';
|
||||
import type {
|
||||
Server,
|
||||
ServerStateData,
|
||||
} from '~/types/server';
|
||||
/**
|
||||
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
|
||||
* @see https://github.com/vuejs/pinia/discussions/1085
|
||||
|
||||
@@ -40,7 +40,7 @@ export interface Server {
|
||||
}
|
||||
|
||||
// @todo convert to object with text and click payload
|
||||
export type ServerStateDataActionType = 'redeem'|'purchase'|'upgrade'|'signOut'|'signIn'|'trialExtend'|'trialStart'|'replace'|'recover';
|
||||
export type ServerStateDataActionType = 'signIn'|'signOut'|'purchase'|'redeem'|'upgrade'|'recover'|'replace'|'trialExtend'|'trialStart';
|
||||
|
||||
export interface ServerStateDataAction {
|
||||
click: any; // @todo be more specific
|
||||
@@ -56,7 +56,7 @@ export interface ServerStateDataError {
|
||||
}
|
||||
|
||||
export interface ServerStateData {
|
||||
actions: ServerStateDataAction[];
|
||||
actions?: ServerStateDataAction[] | undefined;
|
||||
humanReadable: string; // @todo create interface of ENUM to string mapping
|
||||
heading: string;
|
||||
message: string;
|
||||
|
||||
10
types/userProfile.ts
Normal file
10
types/userProfile.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ArrowTopRightOnSquareIcon } from '@heroicons/vue/24/solid';
|
||||
|
||||
export interface UserProfileLink {
|
||||
emphasize?: boolean;
|
||||
external?: boolean;
|
||||
href: string;
|
||||
icon?: typeof ArrowTopRightOnSquareIcon;
|
||||
text: string;
|
||||
title?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user