Browse Source

Merge pull request #3134 from nocodb/fix/gui-v2-column-edit-add

fix(gui-v2): column edit/add handling
pull/3149/head
navi 2 years ago committed by GitHub
parent
commit
abfde8de2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  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. 130
      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. 38
      packages/nc-gui-v2/components/smartsheet-header/CellIcon.vue
  18. 12
      packages/nc-gui-v2/components/smartsheet-header/VirtualCell.vue
  19. 18
      packages/nc-gui-v2/components/smartsheet/Form.vue
  20. 14
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  21. 94
      packages/nc-gui-v2/composables/useColumn.ts
  22. 54
      packages/nc-gui-v2/composables/useColumnCreateStore.ts
  23. 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">
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!
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
formState.value.rqd = !!formState.value.rqd
formState.value.pk = !!formState.value.pk
formState.value.un = !!formState.value.un
formState.value.ai = !!formState.value.ai
formState.value.au = !!formState.value.au
vModel.value.rqd = !!vModel.value.rqd
vModel.value.pk = !!vModel.value.pk
vModel.value.un = !!vModel.value.un
vModel.value.ai = !!vModel.value.ai
vModel.value.au = !!vModel.value.au
</script>
<template>
@ -20,45 +30,37 @@ formState.value.au = !!formState.value.au
<div class="flex justify-space-between">
<a-form-item label="NN">
<a-checkbox
v-model:checked="formState.rqd"
:disabled="formState.pk || !sqlUi.columnEditable(formState)"
v-model:checked="vModel.rqd"
:disabled="vModel.pk || !sqlUi.columnEditable(vModel)"
class="nc-column-checkbox-NN"
@change="onAlter"
/>
</a-form-item>
<a-form-item label="PK">
<a-checkbox
v-model:checked="formState.pk"
:disabled="!sqlUi.columnEditable(formState)"
v-model:checked="vModel.pk"
:disabled="!sqlUi.columnEditable(vModel)"
class="nc-column-checkbox-PK"
@change="onAlter"
/>
</a-form-item>
<a-form-item label="AI">
<a-checkbox
v-model:checked="formState.ai"
:disabled="sqlUi.colPropUNDisabled(formState) || !sqlUi.columnEditable(formState)"
v-model:checked="vModel.ai"
:disabled="sqlUi.colPropUNDisabled(vModel) || !sqlUi.columnEditable(vModel)"
class="nc-column-checkbox-AI"
@change="onAlter"
/>
</a-form-item>
<a-form-item
label="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 label="UN" :disabled="sqlUi.colPropUNDisabled(vModel) || !sqlUi.columnEditable(vModel)" @change="onAlter">
<a-checkbox v-model:checked="vModel.un" class="nc-column-checkbox-UN" />
</a-form-item>
<a-form-item
label="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 label="AU" :disabled="sqlUi.colPropAuDisabled(vModel) || !sqlUi.columnEditable(vModel)" @change="onAlter">
<a-checkbox v-model:checked="vModel.au" class="nc-column-checkbox-AU" />
</a-form-item>
</div>
<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">
{{ type }}
</a-select-option>
@ -66,17 +68,17 @@ formState.value.au = !!formState.value.au
</a-form-item>
<a-form-item :label="$t('labels.lengthValue')">
<a-input
v-model:value="formState.dtxp"
:disabled="sqlUi.getDefaultLengthIsDisabled(formState.dt) || !sqlUi.columnEditable(formState)"
v-model:value="vModel.dtxp"
:disabled="sqlUi.getDefaultLengthIsDisabled(vModel.dt) || !sqlUi.columnEditable(vModel)"
@input="onAlter"
/>
</a-form-item>
<a-form-item v-if="sqlUi.showScale(formState)" label="Scale">
<a-input v-model="formState.dtxs" :disabled="!sqlUi.columnEditable(formState)" @input="onAlter" />
<a-form-item v-if="sqlUi.showScale(vModel)" label="Scale">
<a-input v-model="vModel.dtxs" :disabled="!sqlUi.columnEditable(vModel)" @input="onAlter" />
</a-form-item>
<a-form-item :label="$t('placeholder.defaultValue')">
<a-textarea v-model:value="formState.cdf" auto-size @input="onAlter(2, true)" />
<span class="text-gray-400 text-xs">{{ sqlUi.getDefaultValueForDatatype(formState.dt) }}</span>
<a-textarea v-model:value="vModel.cdf" auto-size @input="onAlter(2, true)" />
<span class="text-gray-400 text-xs">{{ sqlUi.getDefaultValueForDatatype(vModel.dt) }}</span>
</a-form-item>
</div>
</template>

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

@ -1,8 +1,13 @@
<script setup lang="ts">
import { useColumnCreateStoreOrThrow } from '#imports'
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
const iconList = [
@ -37,34 +42,34 @@ const iconList = [
]
const picked = computed({
get: () => formState.value.meta.color,
get: () => vModel.value.meta.color,
set: (val) => {
formState.value.meta.color = val
vModel.value.meta.color = val
},
})
// set default value
formState.value.meta = {
vModel.value.meta = {
icon: {
checked: 'mdi-check-bold',
unchecked: 'mdi-crop-square',
},
color: '#777',
...formState.value.meta,
...vModel.value.meta,
}
// antdv doesn't support object as value
// use iconIdx as value and update back in watch
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(
() => formState.value.meta.iconIdx,
() => vModel.value.meta.iconIdx,
(v) => {
formState.value.meta.icon = iconList[v]
vModel.value.meta.icon = iconList[v]
},
)
</script>
@ -73,20 +78,20 @@ watch(
<a-row>
<a-col :span="24">
<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">
<div class="flex items-center">
<component
:is="getMdiIcon(icon.checked)"
class="mx-1"
:style="{
color: formState.meta.color,
color: vModel.meta.color,
}"
/>
<component
:is="getMdiIcon(icon.unchecked)"
:style="{
color: formState.meta.color,
color: vModel.meta.color,
}"
/>
</div>

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

@ -1,14 +1,20 @@
<script setup lang="ts">
import { useColumnCreateStoreOrThrow, useProject } from '#imports'
import { useProject } from '#imports'
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 {
label: string
value: string
}
const { formState, validateInfos, setAdditionalValidations } = useColumnCreateStoreOrThrow()
const validators = {
'meta.currency_locale': [
{
@ -36,7 +42,8 @@ const validators = {
],
}
// set additional validations
const { setAdditionalValidations, validateInfos } = useColumnCreateStoreOrThrow()
setAdditionalValidations({
...validators,
})
@ -47,7 +54,7 @@ const currencyList = currencyCodes || []
const currencyLocaleList = currencyLocales() || []
const isMoney = computed(() => formState.value.dt === 'money')
const isMoney = computed(() => vModel.value.dt === 'money')
const message = computed(() => {
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
formState.value.meta = {
vModel.value.meta = {
currency_locale: 'en-US',
currency_code: 'USD',
...formState.value.meta,
...vModel.value.meta,
}
</script>
@ -71,7 +78,7 @@ formState.value.meta = {
<a-col :span="12">
<a-form-item v-bind="validateInfos['meta.currency_locale']" label="Currency Locale">
<a-select
v-model:value="formState.meta.currency_locale"
v-model:value="vModel.meta.currency_locale"
class="w-52"
show-search
:filter-option="filterOption"
@ -86,7 +93,7 @@ formState.value.meta = {
<a-col :span="12">
<a-form-item v-bind="validateInfos['meta.currency_code']" label="Currency Code">
<a-select
v-model:value="formState.meta.currency_code"
v-model:value="vModel.meta.currency_code"
class="w-52"
show-search
:filter-option="filterOption"

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

@ -1,18 +1,23 @@
<script setup lang="ts">
import { useColumnCreateStoreOrThrow } from '#imports'
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 (!formState.meta) formState.meta = {}
formState.meta.date_format = dateFormats[0]
if (!vModel.value.meta?.date_format) {
if (!vModel.value.meta) vModel.value.meta = {}
vModel.value.meta.date_format = dateFormats[0]
}
</script>
<template>
<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">
<div class="flex flex-row items-center">
<div class="text-xs">

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

@ -1,8 +1,13 @@
<script setup lang="ts">
import { useColumnCreateStoreOrThrow } from '#imports'
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 =
durationOptions.map((o) => ({
@ -12,9 +17,9 @@ const durationOptionList =
})) || []
// set default value
formState.value.meta = {
vModel.value.meta = {
duration: 0,
...formState.value.meta,
...vModel.value.meta,
}
</script>
@ -25,7 +30,7 @@ formState.value.meta = {
</a-col>
<a-col :span="24">
<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">
{{ duration.title }}
</a-select-option>

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

@ -1,28 +1,26 @@
<script lang="ts" setup>
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 { uiTypes } from '~/utils/columnUtils'
import MdiPlusIcon from '~icons/mdi/plus-circle-outline'
import MdiMinusIcon from '~icons/mdi/minus-circle-outline'
import MdiIdentifierIcon from '~icons/mdi/identifier'
interface Props {
editColumnDropdown?: boolean
}
const { editColumnDropdown } = defineProps<Props>()
const emit = defineEmits(['submit', 'cancel'])
const emit = defineEmits(['cancel', 'submit'])
const meta = inject(MetaInj)
const { formState, generateNewColumnMeta, addOrUpdate, onAlter, onUidtOrIdTypeChange, validateInfos, isEdit } =
useColumnCreateStoreOrThrow()
const reloadDataTrigger = inject(ReloadViewDataHookInj)
const advancedOptions = ref(false)
const { getMeta } = useMetas()
const formulaOptionsRef = ref()
const { formState, validateInfos, onUidtOrIdTypeChange, onAlter, addOrUpdate, generateNewColumnMeta, isEdit } =
useColumnCreateStoreOrThrow()
const editOrAddRef = ref<HTMLElement>()
const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber]
@ -44,18 +42,8 @@ const uiTypesOptions = computed<typeof uiTypes>(() => {
})
const reloadMetaAndData = async () => {
emit('cancel')
await getMeta(meta?.value.id as string, true)
reloadDataTrigger?.trigger()
emit('submit')
}
function onCancel() {
emit('cancel')
if (formState.value.uidt === UITypes.Formula) {
// close formula drawer
formulaOptionsRef.value.formulaSuggestionDrawer = false
}
}
async function onSubmit() {
@ -65,55 +53,69 @@ async function onSubmit() {
setTimeout(() => {
advancedOptions.value = false
}, 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
const antInput = ref()
watchEffect(() => {
if (antInput.value && formState.value) {
// todo: replace setTimeout
setTimeout(() => {
antInput.value.focus()
antInput.value.select()
antInput.value?.focus()
antInput.value?.select()
}, 300)
}
advancedOptions.value = false
})
watch(
() => editColumnDropdown,
(v) => {
if (v) {
if (formState.value.uidt === UITypes.Formula) {
formulaOptionsRef.value.formulaSuggestionDrawer = true
}
}
},
)
// for cases like formula
if (!formState.value?.column_name) {
formState.value.column_name = formState.value?.title
onMounted(() => {
if (isEdit.value === false) {
generateNewColumnMeta()
}
if (formState.value.uidt === UITypes.Formula) {
formulaOptionsRef.value.formulaSuggestionDrawer = true
}
// for cases like formula
if (formState.value && !formState.value.column_name) {
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>
<template>
<div 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">
<div ref="editOrAddRef" class="min-w-[400px] max-h-[95vh] bg-white shadow p-6 overflow-auto" @click.stop>
<a-form v-if="formState" v-model="formState" name="column-create-or-edit" layout="vertical">
<div class="flex flex-col gap-2">
<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-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')"
>
<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>
</a-form-item>
<SmartsheetColumnFormulaOptions v-if="formState.uidt === UITypes.Formula" ref="formulaOptionsRef" />
<SmartsheetColumnCurrencyOptions v-if="formState.uidt === UITypes.Currency" />
<SmartsheetColumnDurationOptions v-if="formState.uidt === UITypes.Duration" />
<SmartsheetColumnRatingOptions v-if="formState.uidt === UITypes.Rating" />
<SmartsheetColumnCheckboxOptions v-if="formState.uidt === UITypes.Checkbox" />
<SmartsheetColumnLookupOptions v-if="!editColumnDropdown && formState.uidt === UITypes.Lookup" />
<SmartsheetColumnDateOptions v-if="formState.uidt === UITypes.Date" />
<SmartsheetColumnRollupOptions v-if="!editColumnDropdown && formState.uidt === UITypes.Rollup" />
<SmartsheetColumnFormulaOptions
v-if="formState.uidt === UITypes.Formula"
ref="formulaOptionsRef"
v-model:value="formState"
/>
<SmartsheetColumnCurrencyOptions v-if="formState.uidt === UITypes.Currency" v-model:value="formState" />
<SmartsheetColumnDurationOptions v-if="formState.uidt === UITypes.Duration" v-model:value="formState" />
<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
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" />
<SmartsheetColumnPercentOptions v-if="formState.uidt === UITypes.Percent" />
<SmartsheetColumnSelectOptions v-if="formState.uidt === UITypes.SingleSelect || formState.uidt === UITypes.MultiSelect" />
<SmartsheetColumnPercentOptions v-if="formState.uidt === UITypes.Percent" v-model:value="formState" />
<SmartsheetColumnSelectOptions
v-if="formState.uidt === UITypes.SingleSelect || formState.uidt === UITypes.MultiSelect"
v-model:value="formState"
/>
</div>
<div
v-if="!isVirtualCol(formState.uidt)"
@ -159,15 +169,15 @@ if (!formState.value?.column_name) {
{{ `Accept only valid ${formState.uidt}` }}
</span>
</a-checkbox>
<SmartsheetColumnAdvancedOptions />
<SmartsheetColumnAdvancedOptions v-model:value="formState" />
</div>
<a-form-item>
<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 -->
{{ $t('general.cancel') }}
</a-button>
<a-button html-type="submit" type="primary" @click="onSubmit">
<a-button html-type="submit" type="primary" @click.prevent="onSubmit">
<!-- Save -->
{{ $t('general.save') }}
</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 type { ColumnType } 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 {
NcAutocompleteTree,
@ -17,6 +17,16 @@ import {
validateDateWithUnknownFormat,
} 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 {
COMPOUND = 'Compound',
IDENTIFIER = 'Identifier',
@ -29,8 +39,6 @@ enum JSEPNode {
ARRAY_EXP = 'ArrayExpression',
}
const { formState, validateInfos, setAdditionalValidations, sqlUi, column } = useColumnCreateStoreOrThrow()
const meta = inject(MetaInj)
const columns = computed(() => meta?.value?.columns || [])
@ -512,11 +520,11 @@ function appendText(item: Record<string, any>) {
const len = wordToComplete.value?.length || 0
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') {
formState.value.formula_raw = insertAtCursor(formulaRef.value.$el, `{${text}}`, len + +!isCurlyBracketBalanced())
vModel.value.formula_raw = insertAtCursor(formulaRef.value.$el, `{${text}}`, len + +!isCurlyBracketBalanced())
} else {
formState.value.formula_raw = insertAtCursor(formulaRef.value.$el, text, len)
vModel.value.formula_raw = insertAtCursor(formulaRef.value.$el, text, len)
}
autocomplete.value = false
wordToComplete.value = ''
@ -582,7 +590,7 @@ function scrollToSelectedOption() {
}
// 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
setAdditionalValidations({
@ -603,7 +611,7 @@ onMounted(() => {
<a-form-item v-bind="validateInfos.formula_raw" label="Formula">
<a-textarea
ref="formulaRef"
v-model:value="formState.formula_raw"
v-model:value="vModel.formula_raw"
class="mb-2"
@keydown.down.prevent="suggestionListDown"
@keydown.up.prevent="suggestionListUp"

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

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

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

@ -1,12 +1,22 @@
<script setup lang="ts">
import type { ColumnType, LinkToAnotherRecordType, TableType } from 'nocodb-sdk'
import { UITypes, isSystemColumn } from 'nocodb-sdk'
import { useColumnCreateStoreOrThrow } from '#imports'
import { MetaInj } from '~/context'
const { formState, validateInfos, onDataTypeChange, setAdditionalValidations } = $(useColumnCreateStoreOrThrow())
const { tables } = $(useProject())
interface Props {
value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
const meta = $(inject(MetaInj)!)
const { setAdditionalValidations, validateInfos, onDataTypeChange } = useColumnCreateStoreOrThrow()
const { tables } = $(useProject())
const { metas } = $(useMetas())
setAdditionalValidations({
@ -14,8 +24,8 @@ setAdditionalValidations({
fk_lookup_column_id: [{ required: true, message: 'Required' }],
})
if (!formState.fk_relation_column_id) formState.fk_relation_column_id = null
if (!formState.fk_lookup_column_id) formState.fk_lookup_column_id = null
if (!vModel.value.fk_relation_column_id) vModel.value.fk_relation_column_id = null
if (!vModel.value.fk_lookup_column_id) vModel.value.fk_lookup_column_id = null
const relationNames = {
mm: 'Many To Many',
@ -42,7 +52,7 @@ const refTables = $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) {
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="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-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">
<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>
@ -67,7 +77,7 @@ const columns = $computed(() => {
</a-select>
</a-form-item>
<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">
{{ column.title }}
</a-select-option>

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

@ -1,20 +1,25 @@
<script setup lang="ts">
import { useColumnCreateStoreOrThrow } from '#imports'
import { precisions } from '@/utils/percentUtils'
const { formState } = $(useColumnCreateStoreOrThrow())
interface Props {
value: Record<string, any>
}
if (!formState.meta) formState.meta = {}
if (!formState.meta?.precision) formState.meta.precision = precisions[0].id
if (!formState.meta?.negative) formState.meta.negative = false
if (!formState.meta?.default) formState.meta.default = null
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
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>
<template>
<div class="flex flex-col mt-2 gap-2">
<div class="flex flex-row space-x-2">
<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">
<div class="flex flex-row items-center">
<div class="text-xs">
@ -25,13 +30,13 @@ if (!formState.meta?.default) formState.meta.default = null
</a-select>
</a-form-item>
<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>
</div>
<div class="flex flex-row mt-2">
<a-form-item>
<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>
</a-form-item>

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

@ -1,8 +1,13 @@
<script setup lang="ts">
import { useColumnCreateStoreOrThrow } from '#imports'
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
const iconList = [
@ -29,14 +34,14 @@ const iconList = [
]
const picked = computed({
get: () => formState.value.meta.color,
get: () => vModel.value.meta.color,
set: (val) => {
formState.value.meta.color = val
vModel.value.meta.color = val
},
})
// set default value
formState.value.meta = {
vModel.value.meta = {
iconIdx: 0,
icon: {
full: 'mdi-star',
@ -44,21 +49,21 @@ formState.value.meta = {
},
color: '#fcb401',
max: 5,
...formState.value.meta,
...vModel.value.meta,
}
// antdv doesn't support object as value
// use iconIdx as value and update back in watch
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(
() => formState.value.meta.iconIdx,
() => vModel.value.meta.iconIdx,
(v) => {
formState.value.meta.icon = iconList[v]
vModel.value.meta.icon = iconList[v]
},
)
</script>
@ -67,20 +72,20 @@ watch(
<a-row :gutter="8">
<a-col :span="12">
<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">
<div class="flex items-center">
<component
:is="getMdiIcon(icon.full)"
class="mx-1"
:style="{
color: formState.meta.color,
color: vModel.meta.color,
}"
/>
<component
:is="getMdiIcon(icon.empty)"
:style="{
color: formState.meta.color,
color: vModel.meta.color,
}"
/>
</div>
@ -90,7 +95,7 @@ watch(
</a-col>
<a-col :span="12">
<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">
{{ v }}
</a-select-option>

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

@ -1,13 +1,22 @@
<script setup lang="ts">
import { UITypes, isSystemColumn, isVirtualCol } from 'nocodb-sdk'
import { inject, useColumnCreateStoreOrThrow, useMetas, useProject } from '#imports'
import { inject, useMetas, useProject } from '#imports'
import { MetaInj } from '~/context'
const { formState, validateInfos, onDataTypeChange, setAdditionalValidations } = $(useColumnCreateStoreOrThrow())
const { tables } = $(useProject())
interface Props {
value: Record<string, any>
}
const props = defineProps<Props>()
const emit = defineEmits(['update:value'])
const vModel = useVModel(props, 'value', emit)
const meta = $(inject(MetaInj)!)
const { setAdditionalValidations, validateInfos, onDataTypeChange } = useColumnCreateStoreOrThrow()
const { tables } = $(useProject())
const { metas } = $(useMetas())
setAdditionalValidations({
@ -33,9 +42,9 @@ const aggrFunctionsList = [
{ text: 'avgDistinct', value: 'avgDistinct' },
]
if (!formState.fk_relation_column_id) formState.fk_relation_column_id = null
if (!formState.fk_rollup_column_id) formState.fk_rollup_column_id = null
if (!formState.rollup_function) formState.rollup_function = null
if (!vModel.value.fk_relation_column_id) vModel.value.fk_relation_column_id = null
if (!vModel.value.fk_rollup_column_id) vModel.value.value.fk_rollup_column_id = null
if (!vModel.value.rollup_function) vModel.value.rollup_function = null
const refTables = $computed(() => {
if (!tables || !tables.length) {
@ -54,7 +63,7 @@ const refTables = $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) {
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="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-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">
<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>
@ -80,7 +89,7 @@ const columns = $computed(() => {
</a-select>
</a-form-item>
<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">
{{ column.title }}
</a-select-option>
@ -88,7 +97,7 @@ const columns = $computed(() => {
</a-form-item>
</div>
<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">
{{ func.text }}
</a-select-option>

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

@ -1,14 +1,21 @@
<script setup lang="ts">
import Draggable from 'vuedraggable'
import { UITypes } from 'nocodb-sdk'
import { useColumnCreateStoreOrThrow } from '#imports'
import { enumColor } from '@/utils'
import MdiDragIcon from '~icons/mdi/drag-vertical'
import MdiArrowDownDropCircle from '~icons/mdi/arrow-down-drop-circle'
import MdiClose from '~icons/mdi/close'
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[]>([])
const colorMenus = $ref<any>({})
@ -24,7 +31,7 @@ const validators = {
if (!opt.title.length) {
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(',')"))
}
if (options.filter((el) => el.title === opt.title).length !== 1) {
@ -64,12 +71,12 @@ const removeOption = (index: number) => {
}
onMounted(() => {
if (!formState.value.colOptions?.options) {
formState.value.colOptions = {
if (!vModel.value.colOptions?.options) {
vModel.value.colOptions = {
options: [],
}
}
options = formState.value.colOptions.options
options = vModel.value.colOptions.options
// Support for older options
for (const op of options.filter((el) => el.order === null)) {
op.title = op.title.replace(/^'/, '').replace(/'$/, '')

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

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

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

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

38
packages/nc-gui-v2/components/smartsheet-header/CellIcon.vue

@ -36,47 +36,47 @@ const additionalColMeta = useColumn(column as Ref<ColumnType>)
const icon = computed(() => {
if (column?.value?.pk) {
return KeyIcon
} else if (additionalColMeta.isJSON) {
} else if (additionalColMeta.isJSON.value) {
return JSONIcon
} else if (additionalColMeta.isDate) {
} else if (additionalColMeta.isDate.value) {
return CalendarIcon
} else if (additionalColMeta.isDateTime) {
} else if (additionalColMeta.isDateTime.value) {
return DatetimeIcon
} else if (additionalColMeta.isSet) {
} else if (additionalColMeta.isSet.value) {
return MultiSelectIcon
} else if (additionalColMeta.isSingleSelect) {
} else if (additionalColMeta.isSingleSelect.value) {
return SingleSelectIcon
} else if (additionalColMeta.isBoolean) {
} else if (additionalColMeta.isBoolean.value) {
return BooleanIcon
} else if (additionalColMeta.isTextArea) {
} else if (additionalColMeta.isTextArea.value) {
return TextAreaIcon
} else if (additionalColMeta.isEmail) {
} else if (additionalColMeta.isEmail.value) {
return EmailIcon
} else if (additionalColMeta.isYear) {
} else if (additionalColMeta.isYear.value) {
return CalendarIcon
} else if (additionalColMeta.isTime) {
} else if (additionalColMeta.isTime.value) {
return ClockIcon
} else if (additionalColMeta.isRating) {
} else if (additionalColMeta.isRating.value) {
return RatingIcon
} else if (additionalColMeta.isAttachment) {
} else if (additionalColMeta.isAttachment.value) {
return AttachmentIcon
} else if (additionalColMeta.isDecimal) {
} else if (additionalColMeta.isDecimal.value) {
return DecimalIcon
} else if (additionalColMeta.isPhoneNumber) {
} else if (additionalColMeta.isPhoneNumber.value) {
return FilePhoneIcon
}
// else if(additionalColMeta.isForeignKey) {
// return FKIcon
// }
else if (additionalColMeta.isURL) {
else if (additionalColMeta.isURL.value) {
return WebIcon
} else if (additionalColMeta.isCurrency) {
} else if (additionalColMeta.isCurrency.value) {
return CurrencyIcon
} else if (additionalColMeta.isPercent) {
} else if (additionalColMeta.isPercent.value) {
return PercentIcon
} else if (additionalColMeta.isInt || additionalColMeta.isFloat) {
} else if (additionalColMeta.isInt.value || additionalColMeta.isFloat.value) {
return NumericIcon
} else if (additionalColMeta.isString) {
} else if (additionalColMeta.isString.value) {
return StringIcon
} else {
return GenericIcon

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 { Ref } from 'vue'
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 }>()
@ -90,8 +90,6 @@ function onVisibleChange() {
// by clicking cancel button
editColumnDropdown.value = true
}
useProvideColumnCreateStore(meta as Ref<TableType>, column)
</script>
<template>
@ -126,12 +124,14 @@ useProvideColumnCreateStore(meta as Ref<TableType>, column)
>
<div />
<template #overlay>
<SmartsheetColumnEditOrAdd
<SmartsheetColumnEditOrAddProvider
v-if="editColumnDropdown"
:column="column"
class="w-full"
:edit-column-dropdown="editColumnDropdown"
@submit="editColumnDropdown = false"
@cancel="editColumnDropdown = false"
@click.stop
@keydown.stop
@cancel="editColumnDropdown = false"
/>
</template>
</a-dropdown>

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

@ -30,8 +30,6 @@ const meta = inject(MetaInj)
const view = inject(ActiveViewInj)
if (meta) useProvideColumnCreateStore(meta)
const { loadFormView, insertRow, formColumnData, formViewData, updateFormView } = useViewData(meta, view as any)
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 editOrAddRef = ref()
const systemFieldsIds = ref<Record<string, any>>([])
const showColumnDropdown = ref(false)
@ -293,10 +289,6 @@ onClickOutside(draggableRef, () => {
activeRow.value = ''
})
onClickOutside(editOrAddRef, () => {
showColumnDropdown.value = false
})
onMounted(async () => {
await loadFormView()
setFormData()
@ -390,7 +382,7 @@ onMounted(async () => {
{{ $t('msg.info.dragDropHide') }}
</div>
<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">
<mdi-plus />
<!-- Add new field to this table -->
@ -398,7 +390,13 @@ onMounted(async () => {
</div>
</a-button>
<template #overlay>
<SmartsheetColumnEditOrAdd ref="editOrAddRef" @submit="submitCallback" @cancel="showColumnDropdown = false" />
<SmartsheetColumnEditOrAddProvider
v-if="showColumnDropdown"
@submit="submitCallback"
@cancel="showColumnDropdown = false"
@click.stop
@keydown.stop
/>
</template>
</a-dropdown>
</template>

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

@ -11,7 +11,6 @@ import {
ref,
useEventListener,
useGridViewColumnWidth,
useProvideColumnCreateStore,
useSmartsheetStoreOrThrow,
useViewData,
watch,
@ -127,9 +126,6 @@ defineExpose({
loadData,
})
// instantiate column create store
if (meta) useProvideColumnCreateStore(meta)
// reset context menu target on hide
watch(contextMenu, () => {
if (!contextMenu.value) {
@ -329,14 +325,20 @@ const expandForm = (row: Row, state: Record<string, any>) => {
</div>
</th>
<!-- 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']">
<div class="h-full w-[60px] flex align-center justify-center">
<MdiPlus class="text-sm" />
</div>
<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>
</a-dropdown>
</th>

94
packages/nc-gui-v2/composables/useColumn.ts

@ -1,55 +1,61 @@
import type { ColumnType } from 'nocodb-sdk'
import { SqlUiFactory, UITypes, isVirtualCol } from 'nocodb-sdk'
import type { Ref } from 'vue'
import type { ComputedRef, Ref } from 'vue'
import { useProject } from '#imports'
export function useColumn(column: Ref<ColumnType>) {
const { project } = useProject()
const uiDatatype: UITypes = column?.value?.uidt as UITypes
const abstractType = isVirtualCol(column?.value)
? null
: SqlUiFactory.create(project.value?.bases?.[0]?.config || { client: 'mysql2' }).getAbstractType(column?.value)
const uiDatatype: ComputedRef<UITypes> = computed(() => column?.value?.uidt as UITypes)
const abstractType = computed(() =>
isVirtualCol(column?.value)
? null
: SqlUiFactory.create(project.value?.bases?.[0]?.config || { client: 'mysql2' }).getAbstractType(column?.value),
)
const dataTypeLow = column?.value?.dt?.toLowerCase()
const isBoolean = abstractType === 'boolean'
const isString = abstractType === 'string'
const isTextArea = uiDatatype === UITypes.LongText
const isInt = abstractType === 'integer'
const isFloat = abstractType === 'float'
const isDate = abstractType === 'date' || uiDatatype === 'Date'
const isYear = abstractType === 'year' || uiDatatype === 'Year'
const isTime = abstractType === 'time' || uiDatatype === 'Time'
const isDateTime = abstractType === 'datetime' || uiDatatype === 'DateTime'
const isJSON = uiDatatype === 'JSON'
const isEnum = uiDatatype === 'SingleSelect'
const isSingleSelect = uiDatatype === 'SingleSelect'
const isSet = uiDatatype === 'MultiSelect'
const isMultiSelect = uiDatatype === 'MultiSelect'
const isURL = uiDatatype === 'URL'
const isEmail = uiDatatype === UITypes.Email
const isAttachment = uiDatatype === 'Attachment'
const isRating = uiDatatype === UITypes.Rating
const isCurrency = uiDatatype === 'Currency'
const isPhoneNumber = uiDatatype === UITypes.PhoneNumber
const isDecimal = uiDatatype === UITypes.Decimal
const isDuration = uiDatatype === UITypes.Duration
const isPercent = uiDatatype === UITypes.Percent
const isAutoSaved = [
UITypes.SingleLineText,
UITypes.LongText,
UITypes.PhoneNumber,
UITypes.Email,
UITypes.URL,
UITypes.Number,
UITypes.Decimal,
UITypes.Percent,
UITypes.Count,
UITypes.AutoNumber,
UITypes.SpecificDBType,
UITypes.Geometry,
].includes(uiDatatype)
const isManualSaved = [UITypes.Currency, UITypes.Year, UITypes.Time, UITypes.Duration].includes(uiDatatype)
const dataTypeLow = computed(() => column?.value?.dt?.toLowerCase())
const isBoolean = computed(() => abstractType.value === 'boolean')
const isString = computed(() => abstractType.value === 'string')
const isTextArea = computed(() => uiDatatype.value === UITypes.LongText)
const isInt = computed(() => abstractType.value === 'integer')
const isFloat = computed(() => abstractType.value === 'float')
const isDate = computed(() => abstractType.value === 'date' || uiDatatype.value === 'Date')
const isYear = computed(() => abstractType.value === 'year' || uiDatatype.value === 'Year')
const isTime = computed(() => abstractType.value === 'time' || uiDatatype.value === 'Time')
const isDateTime = computed(() => abstractType.value === 'datetime' || uiDatatype.value === 'DateTime')
const isJSON = computed(() => uiDatatype.value === 'JSON')
const isEnum = computed(() => uiDatatype.value === 'SingleSelect')
const isSingleSelect = computed(() => uiDatatype.value === 'SingleSelect')
const isSet = computed(() => uiDatatype.value === 'MultiSelect')
const isMultiSelect = computed(() => uiDatatype.value === 'MultiSelect')
const isURL = computed(() => uiDatatype.value === 'URL')
const isEmail = computed(() => uiDatatype.value === UITypes.Email)
const isAttachment = computed(() => uiDatatype.value === 'Attachment')
const isRating = computed(() => uiDatatype.value === UITypes.Rating)
const isCurrency = computed(() => uiDatatype.value === 'Currency')
const isPhoneNumber = computed(() => uiDatatype.value === UITypes.PhoneNumber)
const isDecimal = computed(() => uiDatatype.value === UITypes.Decimal)
const isDuration = computed(() => uiDatatype.value === UITypes.Duration)
const isPercent = computed(() => uiDatatype.value === UITypes.Percent)
const isAutoSaved = computed(() =>
[
UITypes.SingleLineText,
UITypes.LongText,
UITypes.PhoneNumber,
UITypes.Email,
UITypes.URL,
UITypes.Number,
UITypes.Decimal,
UITypes.Percent,
UITypes.Count,
UITypes.AutoNumber,
UITypes.SpecificDBType,
UITypes.Geometry,
].includes(uiDatatype.value),
)
const isManualSaved = computed(() =>
[UITypes.Currency, UITypes.Year, UITypes.Time, UITypes.Duration].includes(uiDatatype.value),
)
return {
abstractType,

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

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

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

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

Loading…
Cancel
Save