Browse Source

Merge pull request #4803 from nocodb/fix/single-multi-select

fix(nc-gui): single multi select behaviours based on roles
pull/4809/head
Raju Udava 2 years ago committed by GitHub
parent
commit
70010a4fc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      packages/nc-gui/components/cell/MultiSelect.vue
  2. 31
      packages/nc-gui/components/cell/SingleSelect.vue

29
packages/nc-gui/components/cell/MultiSelect.vue

@ -19,6 +19,7 @@ import {
useEventListener, useEventListener,
useMetas, useMetas,
useProject, useProject,
useRoles,
useSelectedCellKeyupListener, useSelectedCellKeyupListener,
watch, watch,
} from '#imports' } from '#imports'
@ -57,6 +58,8 @@ const { $api } = useNuxtApp()
const { getMeta } = useMetas() const { getMeta } = useMetas()
const { hasRole } = useRoles()
const { isPg, isMysql } = useProject() const { isPg, isMysql } = useProject()
// a variable to keep newly created options value // a variable to keep newly created options value
@ -80,6 +83,10 @@ const isOptionMissing = computed(() => {
return (options.value ?? []).every((op) => op.title !== searchVal.value) return (options.value ?? []).every((op) => op.title !== searchVal.value)
}) })
const hasEditRoles = computed(() => hasRole('owner', true) || hasRole('creator', true) || hasRole('editor', true))
const editAllowed = computed(() => hasEditRoles.value && (active.value || editable.value))
const vModel = computed({ const vModel = computed({
get: () => { get: () => {
const selected = selectedIds.value.reduce((acc, id) => { const selected = selectedIds.value.reduce((acc, id) => {
@ -154,11 +161,13 @@ watch(
) )
watch(isOpen, (n, _o) => { watch(isOpen, (n, _o) => {
if (editAllowed.value) {
if (!n) { if (!n) {
aselect.value?.$el?.querySelector('input')?.blur() aselect.value?.$el?.querySelector('input')?.blur()
} else { } else {
aselect.value?.$el?.querySelector('input')?.focus() aselect.value?.$el?.querySelector('input')?.focus()
} }
}
}) })
useSelectedCellKeyupListener(active, (e) => { useSelectedCellKeyupListener(active, (e) => {
@ -167,7 +176,7 @@ useSelectedCellKeyupListener(active, (e) => {
isOpen.value = false isOpen.value = false
break break
case 'Enter': case 'Enter':
if (active.value && !isOpen.value) { if (editAllowed.value && active.value && !isOpen.value) {
isOpen.value = true isOpen.value = true
} }
break break
@ -179,6 +188,10 @@ useSelectedCellKeyupListener(active, (e) => {
// skip // skip
break break
default: default:
if (!editAllowed.value) {
e.preventDefault()
break
}
// toggle only if char key pressed // toggle only if char key pressed
if (!(e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) && e.key?.length === 1) { if (!(e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) && e.key?.length === 1) {
e.stopPropagation() e.stopPropagation()
@ -272,14 +285,14 @@ const onTagClick = (e: Event, onClose: Function) => {
:bordered="false" :bordered="false"
clear-icon clear-icon
show-search show-search
:show-arrow="!readOnly" :show-arrow="hasEditRoles && !readOnly && (editable || (active && vModel.length === 0))"
:open="isOpen && (active || editable)" :open="isOpen && (active || editable)"
:disabled="readOnly" :disabled="readOnly"
:class="{ '!ml-[-8px]': readOnly }" :class="{ '!ml-[-8px]': readOnly, 'caret-transparent': !hasEditRoles }"
:dropdown-class-name="`nc-dropdown-multi-select-cell ${isOpen ? 'active' : ''}`" :dropdown-class-name="`nc-dropdown-multi-select-cell ${isOpen ? 'active' : ''}`"
@search="search" @search="search"
@keydown.stop @keydown.stop
@click="isOpen = (active || editable) && !isOpen" @click="isOpen = editAllowed && !isOpen"
> >
<a-select-option <a-select-option
v-for="op of options" v-for="op of options"
@ -303,7 +316,11 @@ const onTagClick = (e: Event, onClose: Function) => {
</a-tag> </a-tag>
</a-select-option> </a-select-option>
<a-select-option v-if="searchVal && isOptionMissing && !isPublic" :key="searchVal" :value="searchVal"> <a-select-option
v-if="searchVal && isOptionMissing && !isPublic && (hasRole('owner', true) || hasRole('creator', true))"
:key="searchVal"
:value="searchVal"
>
<div class="flex gap-2 text-gray-500 items-center h-full"> <div class="flex gap-2 text-gray-500 items-center h-full">
<MdiPlusThick class="min-w-4" /> <MdiPlusThick class="min-w-4" />
<div class="text-xs whitespace-normal"> <div class="text-xs whitespace-normal">
@ -318,7 +335,7 @@ const onTagClick = (e: Event, onClose: Function) => {
class="rounded-tag nc-selected-option" class="rounded-tag nc-selected-option"
:style="{ display: 'flex', alignItems: 'center' }" :style="{ display: 'flex', alignItems: 'center' }"
:color="options.find((el) => el.title === val)?.color" :color="options.find((el) => el.title === val)?.color"
:closable="(active || editable) && (vModel.length > 1 || !column?.rqd)" :closable="editAllowed && (active || editable) && (vModel.length > 1 || !column?.rqd)"
:close-icon="h(MdiCloseCircle, { class: ['ms-close-icon'] })" :close-icon="h(MdiCloseCircle, { class: ['ms-close-icon'] })"
@click="onTagClick($event, onClose)" @click="onTagClick($event, onClose)"
@close="onClose" @close="onClose"

31
packages/nc-gui/components/cell/SingleSelect.vue

@ -14,6 +14,7 @@ import {
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
inject, inject,
ref, ref,
useRoles,
useSelectedCellKeyupListener, useSelectedCellKeyupListener,
watch, watch,
} from '#imports' } from '#imports'
@ -49,6 +50,8 @@ const searchVal = ref()
const { getMeta } = useMetas() const { getMeta } = useMetas()
const { hasRole } = useRoles()
const { isPg, isMysql } = useProject() const { isPg, isMysql } = useProject()
// a variable to keep newly created option value // a variable to keep newly created option value
@ -73,6 +76,10 @@ const isOptionMissing = computed(() => {
return (options.value ?? []).every((op) => op.title !== searchVal.value) return (options.value ?? []).every((op) => op.title !== searchVal.value)
}) })
const hasEditRoles = computed(() => hasRole('owner', true) || hasRole('creator', true) || hasRole('editor', true))
const editAllowed = computed(() => hasEditRoles.value && (active.value || editable.value))
const vModel = computed({ const vModel = computed({
get: () => tempSelectedOptState.value ?? modelValue, get: () => tempSelectedOptState.value ?? modelValue,
set: (val) => { set: (val) => {
@ -87,11 +94,13 @@ const vModel = computed({
}) })
watch(isOpen, (n, _o) => { watch(isOpen, (n, _o) => {
if (editAllowed.value) {
if (!n) { if (!n) {
aselect.value?.$el?.querySelector('input')?.blur() aselect.value?.$el?.querySelector('input')?.blur()
} else { } else {
aselect.value?.$el?.querySelector('input')?.focus() aselect.value?.$el?.querySelector('input')?.focus()
} }
}
}) })
useSelectedCellKeyupListener(active, (e) => { useSelectedCellKeyupListener(active, (e) => {
@ -100,11 +109,15 @@ useSelectedCellKeyupListener(active, (e) => {
isOpen.value = false isOpen.value = false
break break
case 'Enter': case 'Enter':
if (active.value && !isOpen.value) { if (editAllowed.value && active.value && !isOpen.value) {
isOpen.value = true isOpen.value = true
} }
break break
default: default:
if (!editAllowed.value) {
e.preventDefault()
break
}
// toggle only if char key pressed // toggle only if char key pressed
if (!(e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) && e.key?.length === 1) { if (!(e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) && e.key?.length === 1) {
e.stopPropagation() e.stopPropagation()
@ -174,7 +187,7 @@ const toggleMenu = (e: Event) => {
vModel.value = '' vModel.value = ''
return return
} }
isOpen.value = (active.value || editable.value) && !isOpen.value isOpen.value = editAllowed.value && !isOpen.value
} }
</script> </script>
@ -183,11 +196,12 @@ const toggleMenu = (e: Event) => {
ref="aselect" ref="aselect"
v-model:value="vModel" v-model:value="vModel"
class="w-full" class="w-full"
:allow-clear="!column.rqd && active" :class="{ 'caret-transparent': !hasEditRoles }"
:allow-clear="!column.rqd && editAllowed"
:bordered="false" :bordered="false"
:open="isOpen && (active || editable)" :open="isOpen"
:disabled="readOnly" :disabled="readOnly"
:show-arrow="!readOnly && (active || editable || vModel === null)" :show-arrow="hasEditRoles && !readOnly && (editable || (active && vModel === null))"
:dropdown-class-name="`nc-dropdown-single-select-cell ${isOpen ? 'active' : ''}`" :dropdown-class-name="`nc-dropdown-single-select-cell ${isOpen ? 'active' : ''}`"
show-search show-search
@select="isOpen = false" @select="isOpen = false"
@ -216,8 +230,11 @@ const toggleMenu = (e: Event) => {
</span> </span>
</a-tag> </a-tag>
</a-select-option> </a-select-option>
<a-select-option
<a-select-option v-if="searchVal && isOptionMissing && !isPublic" :key="searchVal" :value="searchVal"> v-if="searchVal && isOptionMissing && !isPublic && (hasRole('owner', true) || hasRole('creator', true))"
:key="searchVal"
:value="searchVal"
>
<div class="flex gap-2 text-gray-500 items-center h-full"> <div class="flex gap-2 text-gray-500 items-center h-full">
<MdiPlusThick class="min-w-4" /> <MdiPlusThick class="min-w-4" />
<div class="text-xs whitespace-normal"> <div class="text-xs whitespace-normal">

Loading…
Cancel
Save