Files
api/web/components/UserProfile/DropdownItem.vue
T
Eli Bosley 88087d5201 feat: mount vue apps, not web components (#1639)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Standalone web bundle with auto-mount utilities and a self-contained
test page.
* New responsive modal components for consistent mobile/desktop dialogs.
  * Header actions to copy OS/API versions.

* **Improvements**
* Refreshed UI styles (muted borders), accessibility and animation
refinements.
  * Theming updates and Tailwind v4–aligned, component-scoped styles.
  * Runtime GraphQL endpoint override and CSRF header support.

* **Bug Fixes**
* Safer network fetching and improved manifest/asset loading with
duplicate protection.

* **Tests/Chores**
* Parallel plugin tests, new extractor test suite, and updated
build/test scripts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-03 15:42:21 -04:00

75 lines
2.2 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue';
import { ArrowTopRightOnSquareIcon } from '@heroicons/vue/24/solid';
import { Button } from '@unraid/ui';
import type { ComposerTranslation } from 'vue-i18n';
import type { ServerStateDataAction } from '~/types/server';
import type { UserProfileLink } from '~/types/userProfile';
export interface Props {
item: ServerStateDataAction | UserProfileLink;
rounded?: boolean;
t: ComposerTranslation;
}
const props = withDefaults(defineProps<Props>(), {
rounded: true,
});
const showExternalIconOnHover = computed(() => props.item?.external && props.item.icon !== ArrowTopRightOnSquareIcon);
const buttonClass = computed(() => {
const classes = ['text-left', 'text-sm', 'w-full', 'flex', 'flex-row', 'items-center', 'justify-between', 'gap-x-2', 'px-2', 'py-2', 'h-auto'];
if (!props.item?.emphasize) {
classes.push('dropdown-item-hover');
}
if (props.item?.emphasize) {
classes.push('dropdown-item-emphasized');
}
if (showExternalIconOnHover.value) {
classes.push('group');
}
if (props.rounded) {
classes.push('rounded-md');
}
return classes.join(' ');
});
</script>
<template>
<Button
:as="item?.click ? 'button' : 'a'"
:disabled="item?.disabled"
:href="item?.href ?? null"
:target="item?.external ? '_blank' : null"
:rel="item?.external ? 'noopener noreferrer' : null"
variant="ghost"
:class="buttonClass"
@click.stop="item?.click ? item?.click(item?.clickParams ?? []) : null"
>
<span class="leading-snug inline-flex flex-row items-center gap-x-2">
<component :is="item?.icon" class="shrink-0 text-current w-4 h-4" aria-hidden="true" />
{{ t(item?.text, item?.textParams ?? []) }}
</span>
<ArrowTopRightOnSquareIcon
v-if="showExternalIconOnHover"
class="text-white fill-current shrink-0 w-4 h-4 ml-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 ease-in-out"
/>
</Button>
</template>
<style>
.dropdown-item-hover:hover,
.dropdown-item-hover:focus {
background: linear-gradient(to right, #e22828, #ff8c2f);
}
.dropdown-item-emphasized:hover,
.dropdown-item-emphasized:focus {
background: linear-gradient(to right, rgba(226, 40, 40, 0.6), rgba(255, 140, 47, 0.6));
}
</style>