85 lines
2.1 KiB
Vue
85 lines
2.1 KiB
Vue
<script setup lang="ts">
|
|
import { t } from '@/i18n'
|
|
import { computed } from 'vue'
|
|
import type { RoleItem } from '@/lib/roles'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
roles: RoleItem[]
|
|
sourceId?: number
|
|
canAssign: (role: RoleItem) => boolean
|
|
showLocked?: boolean
|
|
currentId?: number
|
|
cards?: boolean
|
|
label?: string
|
|
}>(),
|
|
{ sourceId: 0, showLocked: false, currentId: undefined, cards: false, label: undefined },
|
|
)
|
|
|
|
const selected = defineModel<number | undefined>('selected', { default: undefined })
|
|
|
|
function reasonFor(role: RoleItem): string | undefined {
|
|
if (role.locked) {
|
|
return t('There is one owner. Transfer ownership to change who it is.')
|
|
}
|
|
return props.canAssign(role)
|
|
? undefined
|
|
: t("Your role can't assign a role with more power than yours.")
|
|
}
|
|
|
|
const items = computed(() =>
|
|
props.roles
|
|
.filter((role) => (props.showLocked || !role.locked) && role.id !== props.sourceId)
|
|
.map((role) => {
|
|
const reason = reasonFor(role)
|
|
return {
|
|
label: role.name,
|
|
value: role.id,
|
|
description: reason ?? role.description,
|
|
disabled: reason !== undefined,
|
|
current: role.id === props.currentId,
|
|
}
|
|
}),
|
|
)
|
|
</script>
|
|
|
|
<template>
|
|
<URadioGroup
|
|
v-if="cards"
|
|
v-model="selected"
|
|
:items="items"
|
|
variant="card"
|
|
color="primary"
|
|
:aria-label="label"
|
|
:ui="{
|
|
item: 'rounded-none has-data-[state=checked]:border-primary has-data-[state=checked]:bg-(--rc-sand) has-data-[state=checked]:text-(--rc-on-sand)',
|
|
fieldset: 'gap-2',
|
|
description: 'text-xs',
|
|
}"
|
|
>
|
|
<template #label="{ item }">
|
|
<span class="flex items-center gap-2">
|
|
{{ item.label }}
|
|
<span v-if="item.current" class="text-xs font-medium">· {{ t('Current') }}</span>
|
|
</span>
|
|
</template>
|
|
</URadioGroup>
|
|
<USelectMenu
|
|
v-else
|
|
v-model="selected"
|
|
:items="items"
|
|
value-key="value"
|
|
:search-input="false"
|
|
:placeholder="t('Choose a role')"
|
|
:portal="false"
|
|
:content="{ position: 'inline' }"
|
|
class="w-full"
|
|
:ui="{
|
|
base: 'h-11 rounded-none md:h-8',
|
|
content: 'rounded-none border border-default',
|
|
item: 'min-h-12 gap-2 px-2 py-1 md:min-h-9',
|
|
itemDescription: 'text-xs text-muted',
|
|
}"
|
|
/>
|
|
</template>
|