Browse Source

feat(gui): set default color and option to add new option in dropdown

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/4406/head
Pranav C 2 years ago
parent
commit
1219657783
  1. 62
      packages/nc-gui/components/cell/MultiSelect.vue
  2. 67
      packages/nc-gui/components/cell/SingleSelect.vue

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

@ -5,7 +5,6 @@ import type { SelectOptionType, SelectOptionsType } from 'nocodb-sdk'
import { import {
ActiveCellInj, ActiveCellInj,
ColumnInj, ColumnInj,
EditModeInj,
IsKanbanInj, IsKanbanInj,
ReadonlyInj, ReadonlyInj,
computed, computed,
@ -16,8 +15,7 @@ import {
useEventListener, useEventListener,
useProject, useProject,
useSelectedCellKeyupListener, useSelectedCellKeyupListener,
watch, watch,enumColor,useMetas
useMetas
} from '#imports' } from '#imports'
import MdiCloseCircle from '~icons/mdi/close-circle' import MdiCloseCircle from '~icons/mdi/close-circle'
@ -30,6 +28,8 @@ const { modelValue } = defineProps<Props>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const emptyOption = Symbol('emptyOption')
const { isMysql } = useProject() const { isMysql } = useProject()
const column = inject(ColumnInj)! const column = inject(ColumnInj)!
@ -48,6 +48,14 @@ const isOpen = ref(false)
const isKanban = inject(IsKanbanInj, ref(false)) const isKanban = inject(IsKanbanInj, ref(false))
const searchVal = ref()
const { $api } = useNuxtApp()
const { getMeta } = useMetas()
const isOptionMissing = computed(() => {
return (options.value ?? []).every((op) => op.title !== searchVal.value)
})
const options = computed<(SelectOptionType & { value?: string })[]>(() => { const options = computed<(SelectOptionType & { value?: string })[]>(() => {
if (column?.value.colOptions) { if (column?.value.colOptions) {
const opts = column.value.colOptions const opts = column.value.colOptions
@ -72,6 +80,9 @@ const vModel = computed({
}, [] as string[]) }, [] as string[])
}, },
set: (val) => { set: (val) => {
if (isOptionMissing.value && val[val.length - 1] === searchVal.value) {
return addIfMissingAndSave()
}
emit('update:modelValue', val.length === 0 ? null : val.join(',')) emit('update:modelValue', val.length === 0 ? null : val.join(','))
}, },
}) })
@ -81,13 +92,13 @@ const selectedTitles = computed(() =>
? typeof modelValue === 'string' ? typeof modelValue === 'string'
? isMysql ? isMysql
? modelValue.split(',').sort((a, b) => { ? modelValue.split(',').sort((a, b) => {
const opa = options.value.find((el) => el.title === a) const opa = options.value.find((el) => el.title === a)
const opb = options.value.find((el) => el.title === b) const opb = options.value.find((el) => el.title === b)
if (opa && opb) { if (opa && opb) {
return opa.order! - opb.order! return opa.order! - opb.order!
} }
return 0 return 0
}) })
: modelValue.split(',') : modelValue.split(',')
: modelValue : modelValue
: [], : [],
@ -162,20 +173,16 @@ useSelectedCellKeyupListener(active, (e) => {
} }
}) })
const searchVal = ref()
const { $api } = useNuxtApp()
const { getMeta } = useMetas()
async function addIfMissingAndSave() { async function addIfMissingAndSave() {
const searchInput = aselect.value?.$el?.querySelector('.ant-select-selection-search-input') if (!searchVal) return false
if (!searchInput) return false const newOptValue = searchVal?.value
const newOptValue = searchInput?.value
if (newOptValue && !options.value.some((o) => o.title === newOptValue)) { if (newOptValue && !options.value.some((o) => o.title === newOptValue)) {
const newOptions = [...options.value] const newOptions = [...options.value]
newOptions.push({ title: newOptValue, value: newOptValue }) newOptions.push({ title: newOptValue, value: newOptValue,
color: enumColor.light[(options.value.length + 1) % enumColor.light.length], })
column.value.colOptions = { options: newOptions.map(({ value: _, ...rest }) => rest) } column.value.colOptions = { options: newOptions.map(({ value: _, ...rest }) => rest) }
await $api.dbTableColumn.update(column.value?.id as string, { await $api.dbTableColumn.update(column.value?.id as string, {
@ -184,9 +191,13 @@ async function addIfMissingAndSave() {
await getMeta(column.value.fk_model_id!, true) await getMeta(column.value.fk_model_id!, true)
vModel.value = [...vModel.value, newOptValue] vModel.value = [...vModel.value, newOptValue]
searchInput.value = '' searchVal.value = ''
} }
} }
const search = () => {
searchVal.value = aselect.value?.$el?.querySelector('.ant-select-selection-search-input')?.value
}
</script> </script>
<template> <template>
@ -203,7 +214,8 @@ async function addIfMissingAndSave() {
:disabled="readOnly" :disabled="readOnly"
:class="{ '!ml-[-8px]': readOnly }" :class="{ '!ml-[-8px]': readOnly }"
:dropdown-class-name="`nc-dropdown-multi-select-cell ${isOpen ? 'active' : ''}`" :dropdown-class-name="`nc-dropdown-multi-select-cell ${isOpen ? 'active' : ''}`"
@keydown.enter.stop @search="search"
@keydown.stop
@click="isOpen = (active || editable) && !isOpen" @click="isOpen = (active || editable) && !isOpen"
> >
<a-select-option <a-select-option
@ -228,6 +240,13 @@ async function addIfMissingAndSave() {
</a-tag> </a-tag>
</a-select-option> </a-select-option>
<a-select-option v-if="searchVal && isOptionMissing" :key="searchVal" :value="searchVal">
<div class="flex gap-2 text-gray-500 items-center">
<MdiPlusThick class="min-w-4" />
<div class="text-xs whitespace-normal"> Create new option named <strong>{{ searchVal }}</strong></div>
</div>
</a-select-option>
<template #tagRender="{ value: val, onClose }"> <template #tagRender="{ value: val, onClose }">
<a-tag <a-tag
v-if="options.find((el) => el.title === val)" v-if="options.find((el) => el.title === val)"
@ -301,6 +320,3 @@ async function addIfMissingAndSave() {
@apply "flex overflow-hidden"; @apply "flex overflow-hidden";
} }
</style> </style>
<!--
-->

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

@ -14,6 +14,7 @@ import {
ref, ref,
useEventListener, useEventListener,
watch, watch,
enumColor,
} from '#imports' } from '#imports'
interface Props { interface Props {
@ -39,13 +40,25 @@ const isOpen = ref(false)
const isKanban = inject(IsKanbanInj, ref(false)) const isKanban = inject(IsKanbanInj, ref(false))
const { $api } = useNuxtApp()
const searchVal = ref()
const isOptionMissing = computed(() => {
return (options.value ?? []).every((op) => op.title !== searchVal.value)
})
const vModel = computed({ const vModel = computed({
get: () => modelValue, get: () => modelValue,
set: (val) => { set: (val) => {
if (isOptionMissing.value && val === searchVal.value) {
return addIfMissingAndSave()
}
emit('update:modelValue', val || null) emit('update:modelValue', val || null)
}, },
}) })
const options = computed<(SelectOptionType & { value: string })[]>(() => { const options = computed<(SelectOptionType & { value: string })[]>(() => {
if (column?.value.colOptions) { if (column?.value.colOptions) {
const opts = column.value.colOptions const opts = column.value.colOptions
@ -68,8 +81,8 @@ const handleKeys = async (e: KeyboardEvent) => {
break break
case 'Enter': case 'Enter':
e.preventDefault() e.preventDefault()
if (await addIfMissingAndSave()) // if (await addIfMissingAndSave())
e.stopPropagation() // e.stopPropagation()
break break
} }
} }
@ -104,46 +117,36 @@ useSelectedCellKeyupListener(active, (e) => {
} }
}) })
const val = ref()
const { $api } = useNuxtApp()
async function addIfMissingAndSave() { async function addIfMissingAndSave() {
const searchInput = aselect.value?.$el?.querySelector('.ant-select-selection-search-input')
if(!searchInput) return false if (!searchVal.value) return false
const newOptValue = searchInput?.value const newOptValue = searchVal.value
searchVal.value = ''
if (newOptValue && !options.value.some((o) => o.title === newOptValue)) { if (newOptValue && !options.value.some((o) => o.title === newOptValue)) {
options.value.push({ title: newOptValue, value: newOptValue }) options.value.push({
title: newOptValue,
value: newOptValue,
color: enumColor.light[(options.value.length + 1) % enumColor.light.length],
})
column.value.colOptions = { options: options.value.map(({ value: _, ...rest }) => rest) } column.value.colOptions = { options: options.value.map(({ value: _, ...rest }) => rest) }
await $api.dbTableColumn.update(column.value?.id as string, { await $api.dbTableColumn.update(column.value?.id as string, {
...column.value, ...column.value,
}) })
vModel.value = newOptValue
} }
} }
</script>
<template>
<!-- <a-auto-complete
ref="aselect"
v-model:value="val"
class="w-full h-fill"
dropdown-class-name="nc-dropdown-single-select-cell"
:options="options"
@select="vModel = val"
:defaultActiveFirstOption="false"
@keydown.enter.prevent.stop="addIfMissingAndSave"
>
<template #option="item">
<div style="display: flex; justify-content: space-between">
{{ item.value }}
</div>
</template>
-->
const search = () => {
searchVal.value = aselect.value?.$el?.querySelector('.ant-select-selection-search-input')?.value
}
</script>
<template>
<a-select <a-select
ref="aselect" ref="aselect"
v-model:value="vModel" v-model:value="vModel"
@ -155,6 +158,9 @@ async function addIfMissingAndSave() {
:show-arrow="!readOnly && (active || editable || vModel === null)" :show-arrow="!readOnly && (active || editable || vModel === null)"
:dropdown-class-name="`nc-dropdown-single-select-cell ${isOpen ? 'active' : ''}`" :dropdown-class-name="`nc-dropdown-single-select-cell ${isOpen ? 'active' : ''}`"
@select="isOpen = false" @select="isOpen = false"
@keydown.stop
show-search
@search="search"
@keydown.enter.stop @keydown.enter.stop
@click="isOpen = (active || editable) && !isOpen" @click="isOpen = (active || editable) && !isOpen"
> >
@ -179,6 +185,15 @@ async function addIfMissingAndSave() {
</span> </span>
</a-tag> </a-tag>
</a-select-option> </a-select-option>
<a-select-option v-if="searchVal && isOptionMissing" :key="searchVal" :value="searchVal">
<div class="flex gap-2 text-gray-500 items-center">
<MdiPlusThick class="min-w-4" />
<div class="text-xs whitespace-normal"> Create new option named <strong>{{ searchVal }}</strong></div>
</div>
</a-select-option>
</a-select> </a-select>
</template> </template>

Loading…
Cancel
Save