mirror of
https://github.com/nocodb/nocodb.git
synced 2026-05-01 08:36:34 +00:00
173 lines
5.0 KiB
Vue
173 lines
5.0 KiB
Vue
<script lang="ts" setup>
|
|
import type { RoleLabels } from 'nocodb-sdk'
|
|
import { RoleDescriptions } from 'nocodb-sdk'
|
|
import type { SelectValue } from 'ant-design-vue/es/select'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
role: keyof typeof RoleLabels
|
|
roles: (keyof typeof RoleLabels)[]
|
|
disabledRoles?: (keyof typeof RoleLabels)[]
|
|
onRoleChange: (role: keyof typeof RoleLabels) => void | Promise<any>
|
|
border?: boolean
|
|
description?: boolean
|
|
inherit?: string
|
|
size?: 'sm' | 'md' | 'lg'
|
|
showInherit?: boolean
|
|
placement?: 'bottomRight' | 'bottomLeft'
|
|
}>(),
|
|
{
|
|
border: true,
|
|
description: true,
|
|
size: 'sm',
|
|
showInherit: false,
|
|
placement: 'bottomLeft',
|
|
},
|
|
)
|
|
|
|
const roleRef = toRef(props, 'role')
|
|
const inheritRef = toRef(props, 'inherit')
|
|
const showInherit = toRef(props, 'showInherit')
|
|
const descriptionRef = toRef(props, 'description')
|
|
const isDropdownOpen = ref(false)
|
|
const dropdownRef = ref(null)
|
|
const sizeRef = toRef(props, 'size')
|
|
|
|
const newRole = ref<null | keyof typeof RoleLabels>(null)
|
|
|
|
async function onChangeRole(val: SelectValue) {
|
|
newRole.value = val as keyof typeof RoleLabels
|
|
|
|
await props.onRoleChange(val as keyof typeof RoleLabels)
|
|
|
|
newRole.value = null
|
|
isDropdownOpen.value = false
|
|
}
|
|
|
|
onClickOutside(dropdownRef, (e) => {
|
|
if ((e.target as HTMLElement)?.closest('.nc-role-selector-dropdown')) {
|
|
return
|
|
}
|
|
|
|
isDropdownOpen.value = false
|
|
})
|
|
|
|
/** Select input will not trigger onChange event if old value is same as new value so manually we have to handle close dropdown */
|
|
const closeOnClickOption = (optionValue: keyof typeof RoleLabels) => {
|
|
if (!!newRole.value || optionValue !== roleRef.value) return
|
|
|
|
isDropdownOpen.value = false
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
ref="dropdownRef"
|
|
size="lg"
|
|
class="nc-roles-selector relative flex items-center gap-3"
|
|
@click="isDropdownOpen = !isDropdownOpen"
|
|
>
|
|
<RolesBadge
|
|
:border="false"
|
|
:inherit="inheritRef === role"
|
|
:role="roleRef"
|
|
:size="sizeRef"
|
|
clickable
|
|
data-testid="roles"
|
|
class="flex-none"
|
|
/>
|
|
<NcTooltip
|
|
v-if="showInherit && inheritRef === role"
|
|
class="uppercase text-[10px] leading-4 text-nc-content-gray-muted"
|
|
placement="bottom"
|
|
>
|
|
<template #title>
|
|
{{ $t('tooltip.roleInheritedFromWorkspace') }}
|
|
</template>
|
|
{{ $t('objects.workspace') }}
|
|
</NcTooltip>
|
|
|
|
<a-select
|
|
:value="roleRef"
|
|
:open="isDropdownOpen"
|
|
:dropdown-match-select-width="false"
|
|
dropdown-class-name="!rounded-lg !h-fit max-w-[350px] nc-role-selector-dropdown"
|
|
class="py-1 !absolute top-0 w-20 h-full z-10 text-xs opacity-0"
|
|
:class="{
|
|
'right-0': placement === 'bottomRight',
|
|
'left-0': placement !== 'bottomRight',
|
|
}"
|
|
:placement="placement"
|
|
@change="onChangeRole"
|
|
>
|
|
<a-select-option v-for="rl in props.disabledRoles || []" :key="rl" :value="rl" disabled>
|
|
<div
|
|
:class="{
|
|
'w-full': descriptionRef,
|
|
'w-[200px]': !descriptionRef,
|
|
}"
|
|
class="flex flex-col nc-role-select-dropdown gap-1"
|
|
>
|
|
<div class="flex items-center justify-between">
|
|
<RolesBadge disabled :border="false" :inherit="inheritRef === rl" :role="rl" />
|
|
<GeneralIcon v-if="!newRole && rl === roleRef" icon="check" class="text-primary" />
|
|
</div>
|
|
<div v-if="descriptionRef" class="text-nc-content-gray-muted text-xs">{{ RoleDescriptions[rl] }}</div>
|
|
</div>
|
|
</a-select-option>
|
|
<a-select-option
|
|
v-for="rl in props.roles"
|
|
:key="rl"
|
|
v-e="['c:workspace:settings:user-role-change']"
|
|
:value="rl"
|
|
:disabled="!!newRole"
|
|
@click="closeOnClickOption(rl)"
|
|
>
|
|
<div
|
|
:class="{
|
|
'w-full': descriptionRef,
|
|
'w-[200px]': !descriptionRef,
|
|
}"
|
|
class="h-full flex flex-col justify-center nc-role-select-dropdown gap-1"
|
|
>
|
|
<div class="flex items-center justify-between">
|
|
<RolesBadge :border="false" :class="`nc-role-select-${rl}`" :inherit="inheritRef === rl" :role="rl" />
|
|
|
|
<GeneralLoader v-if="rl === newRole" size="medium" />
|
|
|
|
<GeneralIcon v-else-if="!newRole && rl === roleRef" icon="check" class="text-primary" />
|
|
</div>
|
|
<div v-if="descriptionRef" class="text-nc-content-gray-muted text-xs">{{ RoleDescriptions[rl] }}</div>
|
|
</div>
|
|
</a-select-option>
|
|
</a-select>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
.ant-select-item-option-content {
|
|
white-space: normal; /* Change from 'nowrap' to 'normal' */
|
|
}
|
|
.nc-role-selector-dropdown {
|
|
.rc-virtual-list-holder {
|
|
&::-webkit-scrollbar {
|
|
width: 4px;
|
|
height: 4px;
|
|
}
|
|
&::-webkit-scrollbar-track-piece {
|
|
width: 0px;
|
|
}
|
|
&::-webkit-scrollbar {
|
|
@apply bg-transparent;
|
|
}
|
|
&::-webkit-scrollbar-thumb {
|
|
width: 4px;
|
|
@apply bg-nc-bg-gray-medium rounded-md;
|
|
}
|
|
&::-webkit-scrollbar-thumb:hover {
|
|
@apply bg-nc-bg-gray-dark;
|
|
}
|
|
}
|
|
}
|
|
</style>
|