Browse Source

fix(gui-v2): column edit/add handling

Signed-off-by: mertmit <mertmit99@gmail.com>
pull/3134/head
mertmit 2 years ago
parent
commit
e95d6b0c6b
  1. 68
      packages/nc-gui-v2/components/smartsheet-column/AdvancedOptions.vue
  2. 31
      packages/nc-gui-v2/components/smartsheet-column/CheckboxOptions.vue
  3. 25
      packages/nc-gui-v2/components/smartsheet-column/CurrencyOptions.vue
  4. 17
      packages/nc-gui-v2/components/smartsheet-column/DateOptions.vue
  5. 15
      packages/nc-gui-v2/components/smartsheet-column/DurationOptions.vue
  6. 118
      packages/nc-gui-v2/components/smartsheet-column/EditOrAdd.vue
  7. 27
      packages/nc-gui-v2/components/smartsheet-column/EditOrAddProvider.vue
  8. 24
      packages/nc-gui-v2/components/smartsheet-column/FormulaOptions.vue
  9. 48
      packages/nc-gui-v2/components/smartsheet-column/LinkedToAnotherRecordOptions.vue
  10. 26
      packages/nc-gui-v2/components/smartsheet-column/LookupOptions.vue
  11. 23
      packages/nc-gui-v2/components/smartsheet-column/PercentOptions.vue
  12. 33
      packages/nc-gui-v2/components/smartsheet-column/RatingOptions.vue
  13. 29
      packages/nc-gui-v2/components/smartsheet-column/RollupOptions.vue
  14. 19
      packages/nc-gui-v2/components/smartsheet-column/SelectOptions.vue
  15. 2
      packages/nc-gui-v2/components/smartsheet-column/SpecificDBTypeOptions.vue
  16. 18
      packages/nc-gui-v2/components/smartsheet-header/Cell.vue
  17. 12
      packages/nc-gui-v2/components/smartsheet-header/VirtualCell.vue
  18. 18
      packages/nc-gui-v2/components/smartsheet/Form.vue
  19. 14
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  20. 50
      packages/nc-gui-v2/composables/useColumnCreateStore.ts
  21. 1
      packages/nc-gui-v2/package.json

68
packages/nc-gui-v2/components/smartsheet-column/AdvancedOptions.vue

@ -1,18 +1,28 @@
<script setup lang="ts"> <script setup lang="ts">
import type { UITypes } from 'nocodb-sdk' import type { UITypes } from 'nocodb-sdk'
import { computed, useColumnCreateStoreOrThrow } from '#imports' import { computed } from '#imports'
const { formState, validateInfos, sqlUi, onDataTypeChange, onAlter } = useColumnCreateStoreOrThrow()! interface Props {
value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
const { sqlUi } = useProject()
const { onAlter, onDataTypeChange, validateInfos } = useColumnCreateStoreOrThrow()
// todo: 2nd argument of `getDataTypeListForUiType` is missing! // todo: 2nd argument of `getDataTypeListForUiType` is missing!
const dataTypes = computed(() => sqlUi?.value?.getDataTypeListForUiType(formState.value as { uidt: UITypes }, '' as any)) const dataTypes = computed(() => sqlUi?.value?.getDataTypeListForUiType(vModel.value as { uidt: UITypes }, '' as any))
// to avoid type error with checkbox // to avoid type error with checkbox
formState.value.rqd = !!formState.value.rqd vModel.value.rqd = !!vModel.value.rqd
formState.value.pk = !!formState.value.pk vModel.value.pk = !!vModel.value.pk
formState.value.un = !!formState.value.un vModel.value.un = !!vModel.value.un
formState.value.ai = !!formState.value.ai vModel.value.ai = !!vModel.value.ai
formState.value.au = !!formState.value.au vModel.value.au = !!vModel.value.au
</script> </script>
<template> <template>
@ -20,45 +30,37 @@ formState.value.au = !!formState.value.au
<div class="flex justify-space-between"> <div class="flex justify-space-between">
<a-form-item label="NN"> <a-form-item label="NN">
<a-checkbox <a-checkbox
v-model:checked="formState.rqd" v-model:checked="vModel.rqd"
:disabled="formState.pk || !sqlUi.columnEditable(formState)" :disabled="vModel.pk || !sqlUi.columnEditable(vModel)"
class="nc-column-checkbox-NN" class="nc-column-checkbox-NN"
@change="onAlter" @change="onAlter"
/> />
</a-form-item> </a-form-item>
<a-form-item label="PK"> <a-form-item label="PK">
<a-checkbox <a-checkbox
v-model:checked="formState.pk" v-model:checked="vModel.pk"
:disabled="!sqlUi.columnEditable(formState)" :disabled="!sqlUi.columnEditable(vModel)"
class="nc-column-checkbox-PK" class="nc-column-checkbox-PK"
@change="onAlter" @change="onAlter"
/> />
</a-form-item> </a-form-item>
<a-form-item label="AI"> <a-form-item label="AI">
<a-checkbox <a-checkbox
v-model:checked="formState.ai" v-model:checked="vModel.ai"
:disabled="sqlUi.colPropUNDisabled(formState) || !sqlUi.columnEditable(formState)" :disabled="sqlUi.colPropUNDisabled(vModel) || !sqlUi.columnEditable(vModel)"
class="nc-column-checkbox-AI" class="nc-column-checkbox-AI"
@change="onAlter" @change="onAlter"
/> />
</a-form-item> </a-form-item>
<a-form-item <a-form-item label="UN" :disabled="sqlUi.colPropUNDisabled(vModel) || !sqlUi.columnEditable(vModel)" @change="onAlter">
label="UN" <a-checkbox v-model:checked="vModel.un" class="nc-column-checkbox-UN" />
:disabled="sqlUi.colPropUNDisabled(formState) || !sqlUi.columnEditable(formState)"
@change="onAlter"
>
<a-checkbox v-model:checked="formState.un" class="nc-column-checkbox-UN" />
</a-form-item> </a-form-item>
<a-form-item <a-form-item label="AU" :disabled="sqlUi.colPropAuDisabled(vModel) || !sqlUi.columnEditable(vModel)" @change="onAlter">
label="AU" <a-checkbox v-model:checked="vModel.au" class="nc-column-checkbox-AU" />
:disabled="sqlUi.colPropAuDisabled(formState) || !sqlUi.columnEditable(formState)"
@change="onAlter"
>
<a-checkbox v-model:checked="formState.au" class="nc-column-checkbox-AU" />
</a-form-item> </a-form-item>
</div> </div>
<a-form-item :label="$t('labels.databaseType')" v-bind="validateInfos.dt"> <a-form-item :label="$t('labels.databaseType')" v-bind="validateInfos.dt">
<a-select v-model:value="formState.dt" @change="onDataTypeChange"> <a-select v-model:value="vModel.dt" @change="onDataTypeChange">
<a-select-option v-for="type in dataTypes" :key="type" :value="type"> <a-select-option v-for="type in dataTypes" :key="type" :value="type">
{{ type }} {{ type }}
</a-select-option> </a-select-option>
@ -66,17 +68,17 @@ formState.value.au = !!formState.value.au
</a-form-item> </a-form-item>
<a-form-item :label="$t('labels.lengthValue')"> <a-form-item :label="$t('labels.lengthValue')">
<a-input <a-input
v-model:value="formState.dtxp" v-model:value="vModel.dtxp"
:disabled="sqlUi.getDefaultLengthIsDisabled(formState.dt) || !sqlUi.columnEditable(formState)" :disabled="sqlUi.getDefaultLengthIsDisabled(vModel.dt) || !sqlUi.columnEditable(vModel)"
@input="onAlter" @input="onAlter"
/> />
</a-form-item> </a-form-item>
<a-form-item v-if="sqlUi.showScale(formState)" label="Scale"> <a-form-item v-if="sqlUi.showScale(vModel)" label="Scale">
<a-input v-model="formState.dtxs" :disabled="!sqlUi.columnEditable(formState)" @input="onAlter" /> <a-input v-model="vModel.dtxs" :disabled="!sqlUi.columnEditable(vModel)" @input="onAlter" />
</a-form-item> </a-form-item>
<a-form-item :label="$t('placeholder.defaultValue')"> <a-form-item :label="$t('placeholder.defaultValue')">
<a-textarea v-model:value="formState.cdf" auto-size @input="onAlter(2, true)" /> <a-textarea v-model:value="vModel.cdf" auto-size @input="onAlter(2, true)" />
<span class="text-gray-400 text-xs">{{ sqlUi.getDefaultValueForDatatype(formState.dt) }}</span> <span class="text-gray-400 text-xs">{{ sqlUi.getDefaultValueForDatatype(vModel.dt) }}</span>
</a-form-item> </a-form-item>
</div> </div>
</template> </template>

31
packages/nc-gui-v2/components/smartsheet-column/CheckboxOptions.vue

@ -1,8 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { useColumnCreateStoreOrThrow } from '#imports'
import { getMdiIcon } from '@/utils' import { getMdiIcon } from '@/utils'
const { formState } = useColumnCreateStoreOrThrow() interface Props {
value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
// cater existing v1 cases // cater existing v1 cases
const iconList = [ const iconList = [
@ -37,34 +42,34 @@ const iconList = [
] ]
const picked = computed({ const picked = computed({
get: () => formState.value.meta.color, get: () => vModel.value.meta.color,
set: (val) => { set: (val) => {
formState.value.meta.color = val vModel.value.meta.color = val
}, },
}) })
// set default value // set default value
formState.value.meta = { vModel.value.meta = {
icon: { icon: {
checked: 'mdi-check-bold', checked: 'mdi-check-bold',
unchecked: 'mdi-crop-square', unchecked: 'mdi-crop-square',
}, },
color: '#777', color: '#777',
...formState.value.meta, ...vModel.value.meta,
} }
// antdv doesn't support object as value // antdv doesn't support object as value
// use iconIdx as value and update back in watch // use iconIdx as value and update back in watch
const iconIdx = iconList.findIndex( const iconIdx = iconList.findIndex(
(ele) => ele.checked === formState.value.meta.icon.checked && ele.unchecked === formState.value.meta.icon.unchecked, (ele) => ele.checked === vModel.value.meta.icon.checked && ele.unchecked === vModel.value.meta.icon.unchecked,
) )
formState.value.meta.iconIdx = iconIdx === -1 ? 0 : iconIdx vModel.value.meta.iconIdx = iconIdx === -1 ? 0 : iconIdx
watch( watch(
() => formState.value.meta.iconIdx, () => vModel.value.meta.iconIdx,
(v) => { (v) => {
formState.value.meta.icon = iconList[v] vModel.value.meta.icon = iconList[v]
}, },
) )
</script> </script>
@ -73,20 +78,20 @@ watch(
<a-row> <a-row>
<a-col :span="24"> <a-col :span="24">
<a-form-item label="Icon"> <a-form-item label="Icon">
<a-select v-model:value="formState.meta.iconIdx" class="w-52"> <a-select v-model:value="vModel.meta.iconIdx" class="w-52">
<a-select-option v-for="(icon, i) of iconList" :key="i" :value="i"> <a-select-option v-for="(icon, i) of iconList" :key="i" :value="i">
<div class="flex items-center"> <div class="flex items-center">
<component <component
:is="getMdiIcon(icon.checked)" :is="getMdiIcon(icon.checked)"
class="mx-1" class="mx-1"
:style="{ :style="{
color: formState.meta.color, color: vModel.meta.color,
}" }"
/> />
<component <component
:is="getMdiIcon(icon.unchecked)" :is="getMdiIcon(icon.unchecked)"
:style="{ :style="{
color: formState.meta.color, color: vModel.meta.color,
}" }"
/> />
</div> </div>

25
packages/nc-gui-v2/components/smartsheet-column/CurrencyOptions.vue

@ -1,14 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { useColumnCreateStoreOrThrow, useProject } from '#imports' import { useProject } from '#imports'
import { currencyCodes, currencyLocales, validateCurrencyCode, validateCurrencyLocale } from '@/utils' import { currencyCodes, currencyLocales, validateCurrencyCode, validateCurrencyLocale } from '@/utils'
interface Props {
value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
interface Option { interface Option {
label: string label: string
value: string value: string
} }
const { formState, validateInfos, setAdditionalValidations } = useColumnCreateStoreOrThrow()
const validators = { const validators = {
'meta.currency_locale': [ 'meta.currency_locale': [
{ {
@ -36,7 +42,8 @@ const validators = {
], ],
} }
// set additional validations const { setAdditionalValidations, validateInfos } = useColumnCreateStoreOrThrow()
setAdditionalValidations({ setAdditionalValidations({
...validators, ...validators,
}) })
@ -47,7 +54,7 @@ const currencyList = currencyCodes || []
const currencyLocaleList = currencyLocales() || [] const currencyLocaleList = currencyLocales() || []
const isMoney = computed(() => formState.value.dt === 'money') const isMoney = computed(() => vModel.value.dt === 'money')
const message = computed(() => { const message = computed(() => {
if (isMoney.value && isPg) return "PostgreSQL 'money' type has own currency settings" if (isMoney.value && isPg) return "PostgreSQL 'money' type has own currency settings"
@ -59,10 +66,10 @@ function filterOption(input: string, option: Option) {
} }
// set default value // set default value
formState.value.meta = { vModel.value.meta = {
currency_locale: 'en-US', currency_locale: 'en-US',
currency_code: 'USD', currency_code: 'USD',
...formState.value.meta, ...vModel.value.meta,
} }
</script> </script>
@ -71,7 +78,7 @@ formState.value.meta = {
<a-col :span="12"> <a-col :span="12">
<a-form-item v-bind="validateInfos['meta.currency_locale']" label="Currency Locale"> <a-form-item v-bind="validateInfos['meta.currency_locale']" label="Currency Locale">
<a-select <a-select
v-model:value="formState.meta.currency_locale" v-model:value="vModel.meta.currency_locale"
class="w-52" class="w-52"
show-search show-search
:filter-option="filterOption" :filter-option="filterOption"
@ -86,7 +93,7 @@ formState.value.meta = {
<a-col :span="12"> <a-col :span="12">
<a-form-item v-bind="validateInfos['meta.currency_code']" label="Currency Code"> <a-form-item v-bind="validateInfos['meta.currency_code']" label="Currency Code">
<a-select <a-select
v-model:value="formState.meta.currency_code" v-model:value="vModel.meta.currency_code"
class="w-52" class="w-52"
show-search show-search
:filter-option="filterOption" :filter-option="filterOption"

17
packages/nc-gui-v2/components/smartsheet-column/DateOptions.vue

@ -1,18 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { useColumnCreateStoreOrThrow } from '#imports'
import { dateFormats } from '~/utils' import { dateFormats } from '~/utils'
const { formState } = $(useColumnCreateStoreOrThrow()) interface Props {
value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
if (!formState.meta?.date_format) { if (!vModel.value.meta?.date_format) {
if (!formState.meta) formState.meta = {} if (!vModel.value.meta) vModel.value.meta = {}
formState.meta.date_format = dateFormats[0] vModel.value.meta.date_format = dateFormats[0]
} }
</script> </script>
<template> <template>
<a-form-item label="Date Format"> <a-form-item label="Date Format">
<a-select v-model:value="formState.meta.date_format"> <a-select v-model:value="vModel.meta.date_format">
<a-select-option v-for="(format, i) of dateFormats" :key="i" :value="format"> <a-select-option v-for="(format, i) of dateFormats" :key="i" :value="format">
<div class="flex flex-row items-center"> <div class="flex flex-row items-center">
<div class="text-xs"> <div class="text-xs">

15
packages/nc-gui-v2/components/smartsheet-column/DurationOptions.vue

@ -1,8 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { useColumnCreateStoreOrThrow } from '#imports'
import { durationOptions } from '@/utils' import { durationOptions } from '@/utils'
const { formState } = useColumnCreateStoreOrThrow() interface Props {
value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
const durationOptionList = const durationOptionList =
durationOptions.map((o) => ({ durationOptions.map((o) => ({
@ -12,9 +17,9 @@ const durationOptionList =
})) || [] })) || []
// set default value // set default value
formState.value.meta = { vModel.value.meta = {
duration: 0, duration: 0,
...formState.value.meta, ...vModel.value.meta,
} }
</script> </script>
@ -25,7 +30,7 @@ formState.value.meta = {
</a-col> </a-col>
<a-col :span="24"> <a-col :span="24">
<a-form-item label="Duration Format"> <a-form-item label="Duration Format">
<a-select v-model:value="formState.meta.duration" class="w-52"> <a-select v-model:value="vModel.meta.duration" class="w-52">
<a-select-option v-for="(duration, i) of durationOptionList" :key="i" :value="duration.id"> <a-select-option v-for="(duration, i) of durationOptionList" :key="i" :value="duration.id">
{{ duration.title }} {{ duration.title }}
</a-select-option> </a-select-option>

118
packages/nc-gui-v2/components/smartsheet-column/EditOrAdd.vue

@ -1,28 +1,26 @@
<script lang="ts" setup> <script lang="ts" setup>
import { UITypes, isVirtualCol } from 'nocodb-sdk' import { UITypes, isVirtualCol } from 'nocodb-sdk'
import { computed, inject, useColumnCreateStoreOrThrow, useMetas, watchEffect } from '#imports' import { computed, inject, useMetas, watchEffect } from '#imports'
import { MetaInj, ReloadViewDataHookInj } from '~/context' import { MetaInj, ReloadViewDataHookInj } from '~/context'
import { uiTypes } from '~/utils/columnUtils' import { uiTypes } from '~/utils/columnUtils'
import MdiPlusIcon from '~icons/mdi/plus-circle-outline' import MdiPlusIcon from '~icons/mdi/plus-circle-outline'
import MdiMinusIcon from '~icons/mdi/minus-circle-outline' import MdiMinusIcon from '~icons/mdi/minus-circle-outline'
import MdiIdentifierIcon from '~icons/mdi/identifier' import MdiIdentifierIcon from '~icons/mdi/identifier'
interface Props { const emit = defineEmits(['submit', 'cancel'])
editColumnDropdown?: boolean
}
const { editColumnDropdown } = defineProps<Props>()
const emit = defineEmits(['cancel', 'submit'])
const meta = inject(MetaInj) const meta = inject(MetaInj)
const { formState, generateNewColumnMeta, addOrUpdate, onAlter, onUidtOrIdTypeChange, validateInfos, isEdit } =
useColumnCreateStoreOrThrow()
const reloadDataTrigger = inject(ReloadViewDataHookInj) const reloadDataTrigger = inject(ReloadViewDataHookInj)
const advancedOptions = ref(false) const advancedOptions = ref(false)
const { getMeta } = useMetas() const { getMeta } = useMetas()
const formulaOptionsRef = ref() const formulaOptionsRef = ref()
const editOrAddRef = ref<HTMLElement>()
const { formState, validateInfos, onUidtOrIdTypeChange, onAlter, addOrUpdate, generateNewColumnMeta, isEdit } =
useColumnCreateStoreOrThrow()
const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber] const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber]
@ -44,18 +42,8 @@ const uiTypesOptions = computed<typeof uiTypes>(() => {
}) })
const reloadMetaAndData = async () => { const reloadMetaAndData = async () => {
emit('cancel')
await getMeta(meta?.value.id as string, true) await getMeta(meta?.value.id as string, true)
reloadDataTrigger?.trigger() reloadDataTrigger?.trigger()
emit('submit')
}
function onCancel() {
emit('cancel')
if (formState.value.uidt === UITypes.Formula) {
// close formula drawer
formulaOptionsRef.value.formulaSuggestionDrawer = false
}
} }
async function onSubmit() { async function onSubmit() {
@ -65,55 +53,69 @@ async function onSubmit() {
setTimeout(() => { setTimeout(() => {
advancedOptions.value = false advancedOptions.value = false
}, 500) }, 500)
emit('submit')
} }
// create column meta if it's a new column
watchEffect(() => {
if (!isEdit.value && formState.value.altered !== 1) {
generateNewColumnMeta()
}
})
// focus and select the column name field // focus and select the column name field
const antInput = ref() const antInput = ref()
watchEffect(() => { watchEffect(() => {
if (antInput.value && formState.value) { if (antInput.value && formState.value) {
// todo: replace setTimeout // todo: replace setTimeout
setTimeout(() => { setTimeout(() => {
antInput.value.focus() antInput.value?.focus()
antInput.value.select() antInput.value?.select()
}, 300) }, 300)
} }
advancedOptions.value = false advancedOptions.value = false
}) })
watch( onMounted(() => {
() => editColumnDropdown, if (isEdit.value === false) {
(v) => { generateNewColumnMeta()
if (v) { }
if (formState.value.uidt === UITypes.Formula) { if (formState.value.uidt === UITypes.Formula) {
formulaOptionsRef.value.formulaSuggestionDrawer = true formulaOptionsRef.value.formulaSuggestionDrawer = true
} }
}
},
)
// for cases like formula // for cases like formula
if (!formState.value?.column_name) { if (formState.value && !formState.value.column_name) {
formState.value.column_name = formState.value?.title formState.value.column_name = formState.value?.title
} }
})
onUnmounted(() => {
if (formState.value.uidt === UITypes.Formula) {
// close formula drawer
formulaOptionsRef.value.formulaSuggestionDrawer = false
}
})
const handleClose = (e: MouseEvent) => {
if (
e.target &&
editOrAddRef?.value &&
!editOrAddRef.value.contains(e.target) &&
!e.target.closest('.ant-dropdown') &&
!e.target.closest('.ant-select') &&
!e.target.closest('.ant-select-item')
) {
emit('cancel')
}
}
useEventListener(document, 'click', handleClose)
</script> </script>
<template> <template>
<div class="min-w-[400px] max-h-[95vh] bg-white shadow p-6 overflow-auto" @click.stop> <div ref="editOrAddRef" class="min-w-[400px] max-h-[95vh] bg-white shadow p-6 overflow-auto" @click.stop>
<a-form v-model="formState" name="column-create-or-edit" layout="vertical"> <a-form v-if="formState" v-model="formState" name="column-create-or-edit" layout="vertical">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<a-form-item :label="$t('labels.columnName')" v-bind="validateInfos.title"> <a-form-item :label="$t('labels.columnName')" v-bind="validateInfos.title">
<a-input ref="antInput" v-model:value="formState.title" class="nc-column-name-input" @input="onAlter(8)" /> <a-input ref="antInput" v-model:value="formState.title" class="nc-column-name-input" @input="onAlter(8)" />
</a-form-item> </a-form-item>
<a-form-item <a-form-item
v-if="!(editColumnDropdown && !!onlyNameUpdateOnEditColumns.find((col) => col === formState.uidt))" v-if="!(isEdit && !!onlyNameUpdateOnEditColumns.find((col) => col === formState.uidt))"
:label="$t('labels.columnType')" :label="$t('labels.columnType')"
> >
<a-select v-model:value="formState.uidt" show-search class="nc-column-type-input" @change="onUidtOrIdTypeChange"> <a-select v-model:value="formState.uidt" show-search class="nc-column-type-input" @change="onUidtOrIdTypeChange">
@ -125,20 +127,28 @@ if (!formState.value?.column_name) {
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<SmartsheetColumnFormulaOptions v-if="formState.uidt === UITypes.Formula" ref="formulaOptionsRef" /> <SmartsheetColumnFormulaOptions
<SmartsheetColumnCurrencyOptions v-if="formState.uidt === UITypes.Currency" /> v-if="formState.uidt === UITypes.Formula"
<SmartsheetColumnDurationOptions v-if="formState.uidt === UITypes.Duration" /> ref="formulaOptionsRef"
<SmartsheetColumnRatingOptions v-if="formState.uidt === UITypes.Rating" /> v-model:value="formState"
<SmartsheetColumnCheckboxOptions v-if="formState.uidt === UITypes.Checkbox" /> />
<SmartsheetColumnLookupOptions v-if="!editColumnDropdown && formState.uidt === UITypes.Lookup" /> <SmartsheetColumnCurrencyOptions v-if="formState.uidt === UITypes.Currency" v-model:value="formState" />
<SmartsheetColumnDateOptions v-if="formState.uidt === UITypes.Date" /> <SmartsheetColumnDurationOptions v-if="formState.uidt === UITypes.Duration" v-model:value="formState" />
<SmartsheetColumnRollupOptions v-if="!editColumnDropdown && formState.uidt === UITypes.Rollup" /> <SmartsheetColumnRatingOptions v-if="formState.uidt === UITypes.Rating" v-model:value="formState" />
<SmartsheetColumnCheckboxOptions v-if="formState.uidt === UITypes.Checkbox" v-model:value="formState" />
<SmartsheetColumnLookupOptions v-if="!isEdit && formState.uidt === UITypes.Lookup" v-model:value="formState" />
<SmartsheetColumnDateOptions v-if="formState.uidt === UITypes.Date" v-model:value="formState" />
<SmartsheetColumnRollupOptions v-if="!isEdit && formState.uidt === UITypes.Rollup" v-model:value="formState" />
<SmartsheetColumnLinkedToAnotherRecordOptions <SmartsheetColumnLinkedToAnotherRecordOptions
v-if="!editColumnDropdown && formState.uidt === UITypes.LinkToAnotherRecord" v-if="!isEdit && formState.uidt === UITypes.LinkToAnotherRecord"
v-model:value="formState"
/> />
<SmartsheetColumnSpecificDBTypeOptions v-if="formState.uidt === UITypes.SpecificDBType" /> <SmartsheetColumnSpecificDBTypeOptions v-if="formState.uidt === UITypes.SpecificDBType" />
<SmartsheetColumnPercentOptions v-if="formState.uidt === UITypes.Percent" /> <SmartsheetColumnPercentOptions v-if="formState.uidt === UITypes.Percent" v-model:value="formState" />
<SmartsheetColumnSelectOptions v-if="formState.uidt === UITypes.SingleSelect || formState.uidt === UITypes.MultiSelect" /> <SmartsheetColumnSelectOptions
v-if="formState.uidt === UITypes.SingleSelect || formState.uidt === UITypes.MultiSelect"
v-model:value="formState"
/>
</div> </div>
<div <div
v-if="!isVirtualCol(formState.uidt)" v-if="!isVirtualCol(formState.uidt)"
@ -159,15 +169,15 @@ if (!formState.value?.column_name) {
{{ `Accept only valid ${formState.uidt}` }} {{ `Accept only valid ${formState.uidt}` }}
</span> </span>
</a-checkbox> </a-checkbox>
<SmartsheetColumnAdvancedOptions /> <SmartsheetColumnAdvancedOptions v-model:value="formState" />
</div> </div>
<a-form-item> <a-form-item>
<div class="flex justify-end gap-1 mt-4"> <div class="flex justify-end gap-1 mt-4">
<a-button html-type="button" @click="onCancel"> <a-button html-type="button" @click="emit('cancel')">
<!-- Cancel --> <!-- Cancel -->
{{ $t('general.cancel') }} {{ $t('general.cancel') }}
</a-button> </a-button>
<a-button html-type="submit" type="primary" @click="onSubmit"> <a-button html-type="submit" type="primary" @click.prevent="onSubmit">
<!-- Save --> <!-- Save -->
{{ $t('general.save') }} {{ $t('general.save') }}
</a-button> </a-button>

27
packages/nc-gui-v2/components/smartsheet-column/EditOrAddProvider.vue

@ -0,0 +1,27 @@
<script lang="ts" setup>
import type { ColumnType, TableType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { inject } from '#imports'
import { MetaInj } from '~/context'
interface Props {
column?: Ref<ColumnType & { meta: any }>
}
const props = defineProps<Props>()
const emit = defineEmits(['submit', 'cancel'])
const meta = inject(MetaInj)
if (props?.column) {
const column = toRef(props, 'column')
useProvideColumnCreateStore(meta as Ref<TableType>, column)
} else {
useProvideColumnCreateStore(meta as Ref<TableType>)
}
</script>
<template>
<SmartsheetColumnEditOrAdd @submit="emit('submit')" @cancel="emit('cancel')"></SmartsheetColumnEditOrAdd>
</template>

24
packages/nc-gui-v2/components/smartsheet-column/FormulaOptions.vue

@ -4,7 +4,7 @@ import type { ListItem as AntListItem } from 'ant-design-vue'
import jsep from 'jsep' import jsep from 'jsep'
import type { ColumnType } from 'nocodb-sdk' import type { ColumnType } from 'nocodb-sdk'
import { UITypes, jsepCurlyHook } from 'nocodb-sdk' import { UITypes, jsepCurlyHook } from 'nocodb-sdk'
import { onMounted, useColumnCreateStoreOrThrow, useDebounceFn } from '#imports' import { onMounted, useDebounceFn } from '#imports'
import { MetaInj } from '~/context' import { MetaInj } from '~/context'
import { import {
NcAutocompleteTree, NcAutocompleteTree,
@ -17,6 +17,16 @@ import {
validateDateWithUnknownFormat, validateDateWithUnknownFormat,
} from '@/utils' } from '@/utils'
interface Props {
value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
const { setAdditionalValidations, validateInfos, sqlUi, column } = useColumnCreateStoreOrThrow()
enum JSEPNode { enum JSEPNode {
COMPOUND = 'Compound', COMPOUND = 'Compound',
IDENTIFIER = 'Identifier', IDENTIFIER = 'Identifier',
@ -29,8 +39,6 @@ enum JSEPNode {
ARRAY_EXP = 'ArrayExpression', ARRAY_EXP = 'ArrayExpression',
} }
const { formState, validateInfos, setAdditionalValidations, sqlUi, column } = useColumnCreateStoreOrThrow()
const meta = inject(MetaInj) const meta = inject(MetaInj)
const columns = computed(() => meta?.value?.columns || []) const columns = computed(() => meta?.value?.columns || [])
@ -512,11 +520,11 @@ function appendText(item: Record<string, any>) {
const len = wordToComplete.value?.length || 0 const len = wordToComplete.value?.length || 0
if (item.type === 'function') { if (item.type === 'function') {
formState.value.formula_raw = insertAtCursor(formulaRef.value.$el, text, len, 1) vModel.value.formula_raw = insertAtCursor(formulaRef.value.$el, text, len, 1)
} else if (item.type === 'column') { } else if (item.type === 'column') {
formState.value.formula_raw = insertAtCursor(formulaRef.value.$el, `{${text}}`, len + +!isCurlyBracketBalanced()) vModel.value.formula_raw = insertAtCursor(formulaRef.value.$el, `{${text}}`, len + +!isCurlyBracketBalanced())
} else { } else {
formState.value.formula_raw = insertAtCursor(formulaRef.value.$el, text, len) vModel.value.formula_raw = insertAtCursor(formulaRef.value.$el, text, len)
} }
autocomplete.value = false autocomplete.value = false
wordToComplete.value = '' wordToComplete.value = ''
@ -582,7 +590,7 @@ function scrollToSelectedOption() {
} }
// set default value // set default value
formState.value.formula_raw = (column?.value?.colOptions as Record<string, any>)?.formula_raw || '' vModel.value.formula_raw = (column?.value?.colOptions as Record<string, any>)?.formula_raw || ''
// set additional validations // set additional validations
setAdditionalValidations({ setAdditionalValidations({
@ -603,7 +611,7 @@ onMounted(() => {
<a-form-item v-bind="validateInfos.formula_raw" label="Formula"> <a-form-item v-bind="validateInfos.formula_raw" label="Formula">
<a-textarea <a-textarea
ref="formulaRef" ref="formulaRef"
v-model:value="formState.formula_raw" v-model:value="vModel.formula_raw"
class="mb-2" class="mb-2"
@keydown.down.prevent="suggestionListDown" @keydown.down.prevent="suggestionListDown"
@keydown.up.prevent="suggestionListUp" @keydown.up.prevent="suggestionListUp"

48
packages/nc-gui-v2/components/smartsheet-column/LinkedToAnotherRecordOptions.vue

@ -1,32 +1,42 @@
<script setup lang="ts"> <script setup lang="ts">
import { ModelTypes, MssqlUi, SqliteUi } from 'nocodb-sdk' import { ModelTypes, MssqlUi, SqliteUi } from 'nocodb-sdk'
import { inject, useColumnCreateStoreOrThrow, useProject } from '#imports' import { inject, useProject } from '#imports'
import { MetaInj } from '~/context' import { MetaInj } from '~/context'
import MdiPlusIcon from '~icons/mdi/plus-circle-outline' import MdiPlusIcon from '~icons/mdi/plus-circle-outline'
import MdiMinusIcon from '~icons/mdi/minus-circle-outline' import MdiMinusIcon from '~icons/mdi/minus-circle-outline'
const { formState, validateInfos, onDataTypeChange, setAdditionalValidations } = $(useColumnCreateStoreOrThrow()) interface Props {
const { tables, sqlUi } = $(useProject()) value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
const meta = $(inject(MetaInj)!) const meta = $(inject(MetaInj)!)
const { setAdditionalValidations, validateInfos, onDataTypeChange } = useColumnCreateStoreOrThrow()
const { tables, sqlUi } = $(useProject())
setAdditionalValidations({ setAdditionalValidations({
childId: [{ required: true, message: 'Required' }], childId: [{ required: true, message: 'Required' }],
}) })
const onUpdateDeleteOptions = sqlUi === MssqlUi ? ['NO ACTION'] : ['NO ACTION', 'CASCADE', 'RESTRICT', 'SET NULL', 'SET DEFAULT'] const onUpdateDeleteOptions = sqlUi === MssqlUi ? ['NO ACTION'] : ['NO ACTION', 'CASCADE', 'RESTRICT', 'SET NULL', 'SET DEFAULT']
if (!formState.parentId) formState.parentId = meta.id if (!vModel.value.parentId) vModel.value.parentId = meta.id
if (!formState.childId) formState.childId = null if (!vModel.value.childId) vModel.value.childId = null
if (!formState.childColumn) formState.childColumn = `${meta.table_name}_id` if (!vModel.value.childColumn) vModel.value.childColumn = `${meta.table_name}_id`
if (!formState.childTable) formState.childTable = meta.table_name if (!vModel.value.childTable) vModel.value.childTable = meta.table_name
if (!formState.parentTable) formState.parentTable = formState.rtn || '' if (!vModel.value.parentTable) vModel.value.parentTable = vModel.value.rtn || ''
if (!formState.parentColumn) formState.parentColumn = formState.rcn || '' if (!vModel.value.parentColumn) vModel.value.parentColumn = vModel.value.rcn || ''
if (!formState.type) formState.type = 'hm' if (!vModel.value.type) vModel.value.type = 'hm'
if (!formState.onUpdate) formState.onUpdate = onUpdateDeleteOptions[0] if (!vModel.value.onUpdate) vModel.value.onUpdate = onUpdateDeleteOptions[0]
if (!formState.onDelete) formState.onDelete = onUpdateDeleteOptions[0] if (!vModel.value.onDelete) vModel.value.onDelete = onUpdateDeleteOptions[0]
if (!formState.virtual) formState.virtual = sqlUi === SqliteUi if (!vModel.value.virtual) vModel.value.virtual = sqlUi === SqliteUi
if (!formState.alias) formState.alias = formState.column_name if (!vModel.value.alias) vModel.value.alias = vModel.value.column_name
const advancedOptions = $(ref(false)) const advancedOptions = $(ref(false))
@ -43,13 +53,13 @@ const refTables = $computed(() => {
<div class="w-full flex flex-col mb-2 mt-4"> <div class="w-full flex flex-col mb-2 mt-4">
<div class="border-2 p-6"> <div class="border-2 p-6">
<a-form-item v-bind="validateInfos.type"> <a-form-item v-bind="validateInfos.type">
<a-radio-group v-model:value="formState.type" name="type" v-bind="validateInfos.type"> <a-radio-group v-model:value="vModel.type" name="type" v-bind="validateInfos.type">
<a-radio value="hm">Has Many</a-radio> <a-radio value="hm">Has Many</a-radio>
<a-radio value="mm">Many To Many</a-radio> <a-radio value="mm">Many To Many</a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item class="flex w-full pb-2 mt-4" :label="$t('labels.childTable')" v-bind="validateInfos.childId"> <a-form-item class="flex w-full pb-2 mt-4" :label="$t('labels.childTable')" v-bind="validateInfos.childId">
<a-select v-model:value="formState.childId" @change="onDataTypeChange"> <a-select v-model:value="vModel.childId" @change="onDataTypeChange">
<a-select-option v-for="(table, index) in refTables" :key="index" :value="table.id"> <a-select-option v-for="(table, index) in refTables" :key="index" :value="table.id">
{{ table.title }} {{ table.title }}
</a-select-option> </a-select-option>
@ -68,14 +78,14 @@ const refTables = $computed(() => {
<div v-if="advancedOptions" class="flex flex-col p-6 gap-4 border-2 mt-2"> <div v-if="advancedOptions" class="flex flex-col p-6 gap-4 border-2 mt-2">
<div class="flex flex-row space-x-2"> <div class="flex flex-row space-x-2">
<a-form-item class="flex w-1/2" :label="$t('labels.onUpdate')"> <a-form-item class="flex w-1/2" :label="$t('labels.onUpdate')">
<a-select v-model:value="formState.onUpdate" :disabled="formState.virtual" name="onUpdate" @change="onDataTypeChange"> <a-select v-model:value="vModel.onUpdate" :disabled="vModel.virtual" name="onUpdate" @change="onDataTypeChange">
<a-select-option v-for="(option, index) in onUpdateDeleteOptions" :key="index" :value="option"> <a-select-option v-for="(option, index) in onUpdateDeleteOptions" :key="index" :value="option">
{{ option }} {{ option }}
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item class="flex w-1/2" :label="$t('labels.onDelete')"> <a-form-item class="flex w-1/2" :label="$t('labels.onDelete')">
<a-select v-model:value="formState.onDelete" :disabled="formState.virtual" name="onDelete" @change="onDataTypeChange"> <a-select v-model:value="vModel.onDelete" :disabled="vModel.virtual" name="onDelete" @change="onDataTypeChange">
<a-select-option v-for="(option, index) in onUpdateDeleteOptions" :key="index" :value="option"> <a-select-option v-for="(option, index) in onUpdateDeleteOptions" :key="index" :value="option">
{{ option }} {{ option }}
</a-select-option> </a-select-option>
@ -84,7 +94,7 @@ const refTables = $computed(() => {
</div> </div>
<div class="flex flex-row"> <div class="flex flex-row">
<a-form-item> <a-form-item>
<a-checkbox v-model:checked="formState.virtual" name="virtual" @change="onDataTypeChange">Virtual Relation</a-checkbox> <a-checkbox v-model:checked="vModel.virtual" name="virtual" @change="onDataTypeChange">Virtual Relation</a-checkbox>
</a-form-item> </a-form-item>
</div> </div>
</div> </div>

26
packages/nc-gui-v2/components/smartsheet-column/LookupOptions.vue

@ -1,12 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ColumnType, LinkToAnotherRecordType, TableType } from 'nocodb-sdk' import type { ColumnType, LinkToAnotherRecordType, TableType } from 'nocodb-sdk'
import { UITypes, isSystemColumn } from 'nocodb-sdk' import { UITypes, isSystemColumn } from 'nocodb-sdk'
import { useColumnCreateStoreOrThrow } from '#imports'
import { MetaInj } from '~/context' import { MetaInj } from '~/context'
const { formState, validateInfos, onDataTypeChange, setAdditionalValidations } = $(useColumnCreateStoreOrThrow()) interface Props {
const { tables } = $(useProject()) value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
const meta = $(inject(MetaInj)!) const meta = $(inject(MetaInj)!)
const { setAdditionalValidations, validateInfos, onDataTypeChange } = useColumnCreateStoreOrThrow()
const { tables } = $(useProject())
const { metas } = $(useMetas()) const { metas } = $(useMetas())
setAdditionalValidations({ setAdditionalValidations({
@ -14,8 +24,8 @@ setAdditionalValidations({
fk_lookup_column_id: [{ required: true, message: 'Required' }], fk_lookup_column_id: [{ required: true, message: 'Required' }],
}) })
if (!formState.fk_relation_column_id) formState.fk_relation_column_id = null if (!vModel.value.fk_relation_column_id) vModel.value.fk_relation_column_id = null
if (!formState.fk_lookup_column_id) formState.fk_lookup_column_id = null if (!vModel.value.fk_lookup_column_id) vModel.value.fk_lookup_column_id = null
const relationNames = { const relationNames = {
mm: 'Many To Many', mm: 'Many To Many',
@ -42,7 +52,7 @@ const refTables = $computed(() => {
}) })
const columns = $computed(() => { const columns = $computed(() => {
const selectedTable = refTables.find((t) => t.column.id === formState.fk_relation_column_id) const selectedTable = refTables.find((t) => t.column.id === vModel.value.fk_relation_column_id)
if (!selectedTable?.id) { if (!selectedTable?.id) {
return [] return []
} }
@ -55,7 +65,7 @@ const columns = $computed(() => {
<div class="p-6 w-full flex flex-col border-2 mb-2 mt-4"> <div class="p-6 w-full flex flex-col border-2 mb-2 mt-4">
<div class="w-full flex flex-row space-x-2"> <div class="w-full flex flex-row space-x-2">
<a-form-item class="flex w-1/2 pb-2" :label="$t('labels.childTable')" v-bind="validateInfos.fk_relation_column_id"> <a-form-item class="flex w-1/2 pb-2" :label="$t('labels.childTable')" v-bind="validateInfos.fk_relation_column_id">
<a-select v-model:value="formState.fk_relation_column_id" dropdown-class-name="!w-64" @change="onDataTypeChange"> <a-select v-model:value="vModel.fk_relation_column_id" dropdown-class-name="!w-64" @change="onDataTypeChange">
<a-select-option v-for="(table, index) in refTables" :key="index" :value="table.col.fk_column_id"> <a-select-option v-for="(table, index) in refTables" :key="index" :value="table.col.fk_column_id">
<div class="flex flex-row space-x-0.5 h-full pb-0.5 items-center justify-between"> <div class="flex flex-row space-x-0.5 h-full pb-0.5 items-center justify-between">
<div class="font-semibold text-xs">{{ table.column.title }}</div> <div class="font-semibold text-xs">{{ table.column.title }}</div>
@ -67,7 +77,7 @@ const columns = $computed(() => {
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item class="flex w-1/2" :label="$t('labels.childColumn')" v-bind="validateInfos.fk_lookup_column_id"> <a-form-item class="flex w-1/2" :label="$t('labels.childColumn')" v-bind="validateInfos.fk_lookup_column_id">
<a-select v-model:value="formState.fk_lookup_column_id" name="fk_lookup_column_id" @change="onDataTypeChange"> <a-select v-model:value="vModel.fk_lookup_column_id" name="fk_lookup_column_id" @change="onDataTypeChange">
<a-select-option v-for="(column, index) of columns" :key="index" :value="column.id"> <a-select-option v-for="(column, index) of columns" :key="index" :value="column.id">
{{ column.title }} {{ column.title }}
</a-select-option> </a-select-option>

23
packages/nc-gui-v2/components/smartsheet-column/PercentOptions.vue

@ -1,20 +1,25 @@
<script setup lang="ts"> <script setup lang="ts">
import { useColumnCreateStoreOrThrow } from '#imports'
import { precisions } from '@/utils/percentUtils' import { precisions } from '@/utils/percentUtils'
const { formState } = $(useColumnCreateStoreOrThrow()) interface Props {
value: Record<string, any>
}
if (!formState.meta) formState.meta = {} const props = defineProps<Props>()
if (!formState.meta?.precision) formState.meta.precision = precisions[0].id const emit = defineEmits(['update:value'])
if (!formState.meta?.negative) formState.meta.negative = false const vModel = useVModel(props, 'value', emit)
if (!formState.meta?.default) formState.meta.default = null
if (!vModel.value.meta) vModel.value.meta = {}
if (!vModel.value.meta?.negative) vModel.value.meta.negative = false
if (!vModel.value.meta?.default) vModel.value.meta.default = null
if (!vModel.value.meta?.precision) vModel.value.meta.precision = precisions[0].id
</script> </script>
<template> <template>
<div class="flex flex-col mt-2 gap-2"> <div class="flex flex-col mt-2 gap-2">
<div class="flex flex-row space-x-2"> <div class="flex flex-row space-x-2">
<a-form-item class="flex w-1/2" label="Precision"> <a-form-item class="flex w-1/2" label="Precision">
<a-select v-model:value="formState.meta.precision"> <a-select v-model:value="vModel.meta.precision">
<a-select-option v-for="(precision, i) of precisions" :key="i" :value="precision.id"> <a-select-option v-for="(precision, i) of precisions" :key="i" :value="precision.id">
<div class="flex flex-row items-center"> <div class="flex flex-row items-center">
<div class="text-xs"> <div class="text-xs">
@ -25,13 +30,13 @@ if (!formState.meta?.default) formState.meta.default = null
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label="Default Number (%)"> <a-form-item label="Default Number (%)">
<a-input v-model:value="formState.meta.default" name="default" type="number" /> <a-input v-model:value="vModel.meta.default" name="default" type="number" />
</a-form-item> </a-form-item>
</div> </div>
<div class="flex flex-row mt-2"> <div class="flex flex-row mt-2">
<a-form-item> <a-form-item>
<div class="flex flex-row space-x-2 items-center"> <div class="flex flex-row space-x-2 items-center">
<a-switch v-model:checked="formState.meta.negative" name="negative" /> <a-switch v-model:checked="vModel.meta.negative" name="negative" />
<div class="text-xs">Allow negative numbers</div> <div class="text-xs">Allow negative numbers</div>
</div> </div>
</a-form-item> </a-form-item>

33
packages/nc-gui-v2/components/smartsheet-column/RatingOptions.vue

@ -1,8 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { useColumnCreateStoreOrThrow } from '#imports'
import { getMdiIcon } from '@/utils' import { getMdiIcon } from '@/utils'
const { formState } = useColumnCreateStoreOrThrow() interface Props {
value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
// cater existing v1 cases // cater existing v1 cases
const iconList = [ const iconList = [
@ -29,14 +34,14 @@ const iconList = [
] ]
const picked = computed({ const picked = computed({
get: () => formState.value.meta.color, get: () => vModel.value.meta.color,
set: (val) => { set: (val) => {
formState.value.meta.color = val vModel.value.meta.color = val
}, },
}) })
// set default value // set default value
formState.value.meta = { vModel.value.meta = {
iconIdx: 0, iconIdx: 0,
icon: { icon: {
full: 'mdi-star', full: 'mdi-star',
@ -44,21 +49,21 @@ formState.value.meta = {
}, },
color: '#fcb401', color: '#fcb401',
max: 5, max: 5,
...formState.value.meta, ...vModel.value.meta,
} }
// antdv doesn't support object as value // antdv doesn't support object as value
// use iconIdx as value and update back in watch // use iconIdx as value and update back in watch
const iconIdx = iconList.findIndex( const iconIdx = iconList.findIndex(
(ele) => ele.full === formState.value.meta.icon.full && ele.empty === formState.value.meta.icon.empty, (ele) => ele.full === vModel.value.meta.icon.full && ele.empty === vModel.value.meta.icon.empty,
) )
formState.value.meta.iconIdx = iconIdx === -1 ? 0 : iconIdx vModel.value.meta.iconIdx = iconIdx === -1 ? 0 : iconIdx
watch( watch(
() => formState.value.meta.iconIdx, () => vModel.value.meta.iconIdx,
(v) => { (v) => {
formState.value.meta.icon = iconList[v] vModel.value.meta.icon = iconList[v]
}, },
) )
</script> </script>
@ -67,20 +72,20 @@ watch(
<a-row :gutter="8"> <a-row :gutter="8">
<a-col :span="12"> <a-col :span="12">
<a-form-item label="Icon"> <a-form-item label="Icon">
<a-select v-model:value="formState.meta.iconIdx" class="w-52"> <a-select v-model:value="vModel.meta.iconIdx" class="w-52">
<a-select-option v-for="(icon, i) of iconList" :key="i" :value="i"> <a-select-option v-for="(icon, i) of iconList" :key="i" :value="i">
<div class="flex items-center"> <div class="flex items-center">
<component <component
:is="getMdiIcon(icon.full)" :is="getMdiIcon(icon.full)"
class="mx-1" class="mx-1"
:style="{ :style="{
color: formState.meta.color, color: vModel.meta.color,
}" }"
/> />
<component <component
:is="getMdiIcon(icon.empty)" :is="getMdiIcon(icon.empty)"
:style="{ :style="{
color: formState.meta.color, color: vModel.meta.color,
}" }"
/> />
</div> </div>
@ -90,7 +95,7 @@ watch(
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-form-item label="Max"> <a-form-item label="Max">
<a-select v-model:value="formState.meta.max" class="w-52"> <a-select v-model:value="vModel.meta.max" class="w-52">
<a-select-option v-for="(v, i) in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" :key="i" :value="v"> <a-select-option v-for="(v, i) in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" :key="i" :value="v">
{{ v }} {{ v }}
</a-select-option> </a-select-option>

29
packages/nc-gui-v2/components/smartsheet-column/RollupOptions.vue

@ -1,13 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import { UITypes, isSystemColumn, isVirtualCol } from 'nocodb-sdk' import { UITypes, isSystemColumn, isVirtualCol } from 'nocodb-sdk'
import { inject, useColumnCreateStoreOrThrow, useMetas, useProject } from '#imports' import { inject, useMetas, useProject } from '#imports'
import { MetaInj } from '~/context' import { MetaInj } from '~/context'
const { formState, validateInfos, onDataTypeChange, setAdditionalValidations } = $(useColumnCreateStoreOrThrow()) interface Props {
const { tables } = $(useProject()) value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
const meta = $(inject(MetaInj)!) const meta = $(inject(MetaInj)!)
const { setAdditionalValidations, validateInfos, onDataTypeChange } = useColumnCreateStoreOrThrow()
const { tables } = $(useProject())
const { metas } = $(useMetas()) const { metas } = $(useMetas())
setAdditionalValidations({ setAdditionalValidations({
@ -33,9 +42,9 @@ const aggrFunctionsList = [
{ text: 'avgDistinct', value: 'avgDistinct' }, { text: 'avgDistinct', value: 'avgDistinct' },
] ]
if (!formState.fk_relation_column_id) formState.fk_relation_column_id = null if (!vModel.value.fk_relation_column_id) vModel.value.fk_relation_column_id = null
if (!formState.fk_rollup_column_id) formState.fk_rollup_column_id = null if (!vModel.value.fk_rollup_column_id) vModel.value.value.fk_rollup_column_id = null
if (!formState.rollup_function) formState.rollup_function = null if (!vModel.value.rollup_function) vModel.value.rollup_function = null
const refTables = $computed(() => { const refTables = $computed(() => {
if (!tables || !tables.length) { if (!tables || !tables.length) {
@ -54,7 +63,7 @@ const refTables = $computed(() => {
}) })
const columns = $computed(() => { const columns = $computed(() => {
const selectedTable = refTables.find((t) => t.column.id === formState.fk_relation_column_id) const selectedTable = refTables.find((t) => t.column.id === vModel.value.fk_relation_column_id)
if (!selectedTable?.id) { if (!selectedTable?.id) {
return [] return []
@ -68,7 +77,7 @@ const columns = $computed(() => {
<div class="p-6 w-full flex flex-col border-2 mb-2 mt-4"> <div class="p-6 w-full flex flex-col border-2 mb-2 mt-4">
<div class="w-full flex flex-row space-x-2"> <div class="w-full flex flex-row space-x-2">
<a-form-item class="flex w-1/2 pb-2" :label="$t('labels.childTable')" v-bind="validateInfos.fk_relation_column_id"> <a-form-item class="flex w-1/2 pb-2" :label="$t('labels.childTable')" v-bind="validateInfos.fk_relation_column_id">
<a-select v-model:value="formState.fk_relation_column_id" dropdown-class-name="!w-64" @change="onDataTypeChange"> <a-select v-model:value="vModel.fk_relation_column_id" dropdown-class-name="!w-64" @change="onDataTypeChange">
<a-select-option v-for="(table, index) in refTables" :key="index" :value="table.col.fk_column_id"> <a-select-option v-for="(table, index) in refTables" :key="index" :value="table.col.fk_column_id">
<div class="flex flex-row space-x-0.5 h-full pb-0.5 items-center justify-between"> <div class="flex flex-row space-x-0.5 h-full pb-0.5 items-center justify-between">
<div class="font-semibold text-xs">{{ table.column.title }}</div> <div class="font-semibold text-xs">{{ table.column.title }}</div>
@ -80,7 +89,7 @@ const columns = $computed(() => {
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item class="flex w-1/2" :label="$t('labels.childColumn')" v-bind="validateInfos.fk_rollup_column_id"> <a-form-item class="flex w-1/2" :label="$t('labels.childColumn')" v-bind="validateInfos.fk_rollup_column_id">
<a-select v-model:value="formState.fk_rollup_column_id" name="fk_rollup_column_id" @change="onDataTypeChange"> <a-select v-model:value="vModel.fk_rollup_column_id" name="fk_rollup_column_id" @change="onDataTypeChange">
<a-select-option v-for="(column, index) of columns" :key="index" :value="column.id"> <a-select-option v-for="(column, index) of columns" :key="index" :value="column.id">
{{ column.title }} {{ column.title }}
</a-select-option> </a-select-option>
@ -88,7 +97,7 @@ const columns = $computed(() => {
</a-form-item> </a-form-item>
</div> </div>
<a-form-item label="Aggregate function" v-bind="validateInfos.rollup_function"> <a-form-item label="Aggregate function" v-bind="validateInfos.rollup_function">
<a-select v-model:value="formState.rollup_function" @change="onDataTypeChange"> <a-select v-model:value="vModel.rollup_function" @change="onDataTypeChange">
<a-select-option v-for="(func, index) of aggrFunctionsList" :key="index" :value="func.value"> <a-select-option v-for="(func, index) of aggrFunctionsList" :key="index" :value="func.value">
{{ func.text }} {{ func.text }}
</a-select-option> </a-select-option>

19
packages/nc-gui-v2/components/smartsheet-column/SelectOptions.vue

@ -1,14 +1,21 @@
<script setup lang="ts"> <script setup lang="ts">
import Draggable from 'vuedraggable' import Draggable from 'vuedraggable'
import { UITypes } from 'nocodb-sdk' import { UITypes } from 'nocodb-sdk'
import { useColumnCreateStoreOrThrow } from '#imports'
import { enumColor } from '@/utils' import { enumColor } from '@/utils'
import MdiDragIcon from '~icons/mdi/drag-vertical' import MdiDragIcon from '~icons/mdi/drag-vertical'
import MdiArrowDownDropCircle from '~icons/mdi/arrow-down-drop-circle' import MdiArrowDownDropCircle from '~icons/mdi/arrow-down-drop-circle'
import MdiClose from '~icons/mdi/close' import MdiClose from '~icons/mdi/close'
import MdiPlusIcon from '~icons/mdi/plus' import MdiPlusIcon from '~icons/mdi/plus'
const { formState, setAdditionalValidations } = useColumnCreateStoreOrThrow() interface Props {
value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
const { setAdditionalValidations } = useColumnCreateStoreOrThrow()
let options = $ref<any[]>([]) let options = $ref<any[]>([])
const colorMenus = $ref<any>({}) const colorMenus = $ref<any>({})
@ -24,7 +31,7 @@ const validators = {
if (!opt.title.length) { if (!opt.title.length) {
return reject(new Error("Select options can't be null")) return reject(new Error("Select options can't be null"))
} }
if (formState.value.uidt === UITypes.MultiSelect && opt.title.includes(',')) { if (vModel.value.uidt === UITypes.MultiSelect && opt.title.includes(',')) {
return reject(new Error("MultiSelect columns can't have commas(',')")) return reject(new Error("MultiSelect columns can't have commas(',')"))
} }
if (options.filter((el) => el.title === opt.title).length !== 1) { if (options.filter((el) => el.title === opt.title).length !== 1) {
@ -64,12 +71,12 @@ const removeOption = (index: number) => {
} }
onMounted(() => { onMounted(() => {
if (!formState.value.colOptions?.options) { if (!vModel.value.colOptions?.options) {
formState.value.colOptions = { vModel.value.colOptions = {
options: [], options: [],
} }
} }
options = formState.value.colOptions.options options = vModel.value.colOptions.options
// Support for older options // Support for older options
for (const op of options.filter((el) => el.order === null)) { for (const op of options.filter((el) => el.order === null)) {
op.title = op.title.replace(/^'/, '').replace(/'$/, '') op.title = op.title.replace(/^'/, '').replace(/'$/, '')

2
packages/nc-gui-v2/components/smartsheet-column/SpecificDBTypeOptions.vue

@ -1,3 +1,3 @@
<template> <template>
<SmartsheetColumnAdvancedOptions class="mt-4 mb-2" /> <div class="mt-4 mb-2" />
</template> </template>

18
packages/nc-gui-v2/components/smartsheet-header/Cell.vue

@ -1,10 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ColumnType, TableType } from 'nocodb-sdk' import type { ColumnType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { inject, toRef } from 'vue' import { inject, toRef } from 'vue'
import { ColumnInj, IsFormInj, MetaInj } from '~/context' import { ColumnInj, IsFormInj } from '~/context'
import { useProvideColumnCreateStore } from '#imports'
const props = defineProps<{ column: ColumnType & { meta: any }; required?: boolean; hideMenu?: boolean }>() const props = defineProps<{ column: ColumnType & { meta: any }; required?: boolean; hideMenu?: boolean }>()
const hideMenu = toRef(props, 'hideMenu') const hideMenu = toRef(props, 'hideMenu')
@ -26,9 +23,6 @@ function onVisibleChange() {
// by clicking cancel button // by clicking cancel button
editColumnDropdown.value = true editColumnDropdown.value = true
} }
// instantiate column update store
useProvideColumnCreateStore(meta as Ref<TableType>, column)
</script> </script>
<template> <template>
@ -50,12 +44,14 @@ useProvideColumnCreateStore(meta as Ref<TableType>, column)
> >
<div /> <div />
<template #overlay> <template #overlay>
<SmartsheetColumnEditOrAdd <SmartsheetColumnEditOrAddProvider
v-if="editColumnDropdown"
:column="column"
class="w-full" class="w-full"
:edit-column-dropdown="editColumnDropdown" @submit="editColumnDropdown = false"
@cancel="editColumnDropdown = false"
@click.stop @click.stop
@keydown.stop @keydown.stop
@cancel="editColumnDropdown = false"
/> />
</template> </template>
</a-dropdown> </a-dropdown>

12
packages/nc-gui-v2/components/smartsheet-header/VirtualCell.vue

@ -3,7 +3,7 @@ import { substituteColumnIdWithAliasInFormula } from 'nocodb-sdk'
import type { ColumnType, FormulaType, LinkToAnotherRecordType, LookupType, RollupType, TableType } from 'nocodb-sdk' import type { ColumnType, FormulaType, LinkToAnotherRecordType, LookupType, RollupType, TableType } from 'nocodb-sdk'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { ColumnInj, IsFormInj, MetaInj } from '~/context' import { ColumnInj, IsFormInj, MetaInj } from '~/context'
import { provide, toRef, useMetas, useProvideColumnCreateStore } from '#imports' import { provide, toRef, useMetas } from '#imports'
const props = defineProps<{ column: ColumnType & { meta: any }; hideMenu?: boolean; required?: boolean }>() const props = defineProps<{ column: ColumnType & { meta: any }; hideMenu?: boolean; required?: boolean }>()
@ -90,8 +90,6 @@ function onVisibleChange() {
// by clicking cancel button // by clicking cancel button
editColumnDropdown.value = true editColumnDropdown.value = true
} }
useProvideColumnCreateStore(meta as Ref<TableType>, column)
</script> </script>
<template> <template>
@ -126,12 +124,14 @@ useProvideColumnCreateStore(meta as Ref<TableType>, column)
> >
<div /> <div />
<template #overlay> <template #overlay>
<SmartsheetColumnEditOrAdd <SmartsheetColumnEditOrAddProvider
v-if="editColumnDropdown"
:column="column"
class="w-full" class="w-full"
:edit-column-dropdown="editColumnDropdown" @submit="editColumnDropdown = false"
@cancel="editColumnDropdown = false"
@click.stop @click.stop
@keydown.stop @keydown.stop
@cancel="editColumnDropdown = false"
/> />
</template> </template>
</a-dropdown> </a-dropdown>

18
packages/nc-gui-v2/components/smartsheet/Form.vue

@ -30,8 +30,6 @@ const meta = inject(MetaInj)
const view = inject(ActiveViewInj) const view = inject(ActiveViewInj)
if (meta) useProvideColumnCreateStore(meta)
const { loadFormView, insertRow, formColumnData, formViewData, updateFormView } = useViewData(meta, view as any) const { loadFormView, insertRow, formColumnData, formViewData, updateFormView } = useViewData(meta, view as any)
const { showAll, hideAll, saveOrUpdate } = useViewColumns(view, meta as any, false, async () => { const { showAll, hideAll, saveOrUpdate } = useViewColumns(view, meta as any, false, async () => {
@ -47,8 +45,6 @@ const hiddenColumns = ref<Record<string, any>>([])
const draggableRef = ref() const draggableRef = ref()
const editOrAddRef = ref()
const systemFieldsIds = ref<Record<string, any>>([]) const systemFieldsIds = ref<Record<string, any>>([])
const showColumnDropdown = ref(false) const showColumnDropdown = ref(false)
@ -293,10 +289,6 @@ onClickOutside(draggableRef, () => {
activeRow.value = '' activeRow.value = ''
}) })
onClickOutside(editOrAddRef, () => {
showColumnDropdown.value = false
})
onMounted(async () => { onMounted(async () => {
await loadFormView() await loadFormView()
setFormData() setFormData()
@ -390,7 +382,7 @@ onMounted(async () => {
{{ $t('msg.info.dragDropHide') }} {{ $t('msg.info.dragDropHide') }}
</div> </div>
<a-dropdown v-model:visible="showColumnDropdown" :trigger="['click']"> <a-dropdown v-model:visible="showColumnDropdown" :trigger="['click']">
<a-button type="link" class="w-full caption mt-2" size="large" @click="showColumnDropdown = true"> <a-button type="link" class="w-full caption mt-2" size="large" @click.stop="showColumnDropdown = true">
<div class="flex items-center prose-sm justify-center text-gray-400"> <div class="flex items-center prose-sm justify-center text-gray-400">
<mdi-plus /> <mdi-plus />
<!-- Add new field to this table --> <!-- Add new field to this table -->
@ -398,7 +390,13 @@ onMounted(async () => {
</div> </div>
</a-button> </a-button>
<template #overlay> <template #overlay>
<SmartsheetColumnEditOrAdd ref="editOrAddRef" @submit="submitCallback" @cancel="showColumnDropdown = false" /> <SmartsheetColumnEditOrAddProvider
v-if="showColumnDropdown"
@submit="submitCallback"
@cancel="showColumnDropdown = false"
@click.stop
@keydown.stop
/>
</template> </template>
</a-dropdown> </a-dropdown>
</template> </template>

14
packages/nc-gui-v2/components/smartsheet/Grid.vue

@ -11,7 +11,6 @@ import {
ref, ref,
useEventListener, useEventListener,
useGridViewColumnWidth, useGridViewColumnWidth,
useProvideColumnCreateStore,
useSmartsheetStoreOrThrow, useSmartsheetStoreOrThrow,
useViewData, useViewData,
watch, watch,
@ -127,9 +126,6 @@ defineExpose({
loadData, loadData,
}) })
// instantiate column create store
if (meta) useProvideColumnCreateStore(meta)
// reset context menu target on hide // reset context menu target on hide
watch(contextMenu, () => { watch(contextMenu, () => {
if (!contextMenu.value) { if (!contextMenu.value) {
@ -329,14 +325,20 @@ const expandForm = (row: Row, state: Record<string, any>) => {
</div> </div>
</th> </th>
<!-- v-if="!isLocked && !isVirtual && !isPublicView && _isUIAllowed('add-column')" --> <!-- v-if="!isLocked && !isVirtual && !isPublicView && _isUIAllowed('add-column')" -->
<th v-if="isUIAllowed('add-column')" v-t="['c:column:add']" @click="addColumnDropdown = true"> <th v-if="isUIAllowed('add-column')" v-t="['c:column:add']" @click.stop="addColumnDropdown = true">
<a-dropdown v-model:visible="addColumnDropdown" :trigger="['click']"> <a-dropdown v-model:visible="addColumnDropdown" :trigger="['click']">
<div class="h-full w-[60px] flex align-center justify-center"> <div class="h-full w-[60px] flex align-center justify-center">
<MdiPlus class="text-sm" /> <MdiPlus class="text-sm" />
</div> </div>
<template #overlay> <template #overlay>
<SmartsheetColumnEditOrAdd @click.stop @keydown.stop @cancel="addColumnDropdown = false" /> <SmartsheetColumnEditOrAddProvider
v-if="addColumnDropdown"
@submit="addColumnDropdown = false"
@cancel="addColumnDropdown = false"
@click.stop
@keydown.stop
/>
</template> </template>
</a-dropdown> </a-dropdown>
</th> </th>

50
packages/nc-gui-v2/composables/useColumnCreateStore.ts

@ -1,4 +1,5 @@
import { createInjectionState } from '@vueuse/core' import { createInjectionState } from '@vueuse/core'
import clone from 'just-clone'
import { Form, message } from 'ant-design-vue' import { Form, message } from 'ant-design-vue'
import type { ColumnType, TableType } from 'nocodb-sdk' import type { ColumnType, TableType } from 'nocodb-sdk'
import { UITypes } from 'nocodb-sdk' import { UITypes } from 'nocodb-sdk'
@ -25,18 +26,28 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
const { $api } = useNuxtApp() const { $api } = useNuxtApp()
const { getMeta } = useMetas() const { getMeta } = useMetas()
const isEdit = computed(() => !!column?.value?.id)
const idType = null const idType = null
// state const additionalValidations = ref<Record<string, any>>({})
// todo: give proper type - ColumnType
const setAdditionalValidations = (validations: Record<string, any>) => {
additionalValidations.value = validations
}
const formState = ref<Record<string, any>>({ const formState = ref<Record<string, any>>({
title: 'title', title: 'title',
uidt: UITypes.SingleLineText, uidt: UITypes.SingleLineText,
...(column?.value || {}), ...clone(column?.value || {}),
meta: column?.value?.meta || {},
}) })
const additionalValidations = ref<Record<string, any>>({}) // actions
const generateNewColumnMeta = () => {
setAdditionalValidations({})
formState.value = { meta: {}, ...sqlUi.value.getNewColumn((meta.value?.columns?.length || 0) + 1) }
formState.value.title = formState.value.column_name
}
const validators = computed(() => { const validators = computed(() => {
return { return {
@ -77,17 +88,6 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
const { resetFields, validate, validateInfos } = useForm(formState, validators) const { resetFields, validate, validateInfos } = useForm(formState, validators)
const setAdditionalValidations = (validations: Record<string, any>) => {
additionalValidations.value = validations
}
// actions
const generateNewColumnMeta = () => {
setAdditionalValidations({})
formState.value = { meta: {}, ...sqlUi.value.getNewColumn((meta.value?.columns?.length || 0) + 1) }
formState.value.title = formState.value.title || formState.value.column_name
}
const onUidtOrIdTypeChange = () => { const onUidtOrIdTypeChange = () => {
const { isCurrency } = useColumn(ref(formState.value as ColumnType)) const { isCurrency } = useColumn(ref(formState.value as ColumnType))
@ -177,6 +177,8 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
console.log(formState, validators) console.log(formState, validators)
if (!(await validate())) return if (!(await validate())) return
} catch (e) { } catch (e) {
console.log(e)
console.trace()
message.error('Form validation failed') message.error('Form validation failed')
return return
} }
@ -220,18 +222,18 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
return { return {
formState, formState,
generateNewColumnMeta,
addOrUpdate,
onAlter,
onDataTypeChange,
onUidtOrIdTypeChange,
setAdditionalValidations,
resetFields, resetFields,
validate, validate,
validateInfos, validateInfos,
setAdditionalValidations, isEdit,
onUidtOrIdTypeChange,
sqlUi,
onDataTypeChange,
onAlter,
addOrUpdate,
generateNewColumnMeta,
isEdit: computed(() => !!column?.value?.id),
column, column,
sqlUi,
} }
}, },
) )

1
packages/nc-gui-v2/package.json

@ -18,6 +18,7 @@
"dayjs": "^1.11.3", "dayjs": "^1.11.3",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"jsep": "^1.3.6", "jsep": "^1.3.6",
"just-clone": "^6.1.1",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"locale-codes": "^1.3.1", "locale-codes": "^1.3.1",
"monaco-editor": "^0.33.0", "monaco-editor": "^0.33.0",

Loading…
Cancel
Save