Browse Source

Merge pull request #2857 from nocodb/feat/gui-v2-column-create-edit

feat(gui-v2): column create/edit
pull/2931/head
navi 2 years ago committed by GitHub
parent
commit
48459a05d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      packages/nc-gui-v2/assets/style-v2.scss
  2. 94
      packages/nc-gui-v2/components/smartsheet-column/AdvancedOptions.vue
  3. 145
      packages/nc-gui-v2/components/smartsheet-column/EditOrAdd.vue
  4. 101
      packages/nc-gui-v2/components/smartsheet-header/Cell.vue
  5. 42
      packages/nc-gui-v2/components/smartsheet-header/Menu.vue
  6. 4
      packages/nc-gui-v2/components/smartsheet-toolbar/FieldsMenu.vue
  7. 32
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  8. 223
      packages/nc-gui-v2/composables/useColumnCreateStore.ts
  9. 2
      packages/nc-gui-v2/composables/useGlobalState/index.ts
  10. 2
      packages/nc-gui-v2/composables/useGridViewColumnWidth.ts
  11. 7
      packages/nc-gui-v2/composables/useProject.ts
  12. 8
      packages/nc-gui-v2/composables/useViewColumns.ts
  13. 2
      packages/nc-gui-v2/context/index.ts
  14. 189
      packages/nc-gui-v2/utils/columnUtils.ts
  15. 2
      packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts
  16. 2
      packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts
  17. 2
      packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts
  18. 2
      packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts

3
packages/nc-gui-v2/assets/style-v2.scss

@ -72,9 +72,8 @@ html {
overflow-y: auto !important;
}
.nc-menu-item {
@apply cursor-pointer text-xs flex align-center gap-2 p-4 relative after:(content-[''] absolute top-0 left-0 w-full h-full right 0 bg-current opacity-0 transition transition-opactity duration-100) hover:(after:(opacity-5));
@apply cursor-pointer text-xs flex align-center gap-2 px-4 py-3 relative after:(content-[''] absolute top-0 left-0 w-full h-full right 0 bg-current opacity-0 transition transition-opactity duration-100) hover:(after:(opacity-5));
}
.nc-sidebar-right-item {

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

@ -0,0 +1,94 @@
<script setup lang="ts">
import { useColumnCreateStoreOrThrow } from '~/composables/useColumnCreateStore'
const { formState, validateInfos, setAdditionalValidations, sqlUi, onDataTypeChange, onAlter } = useColumnCreateStoreOrThrow()
const dataTypes = computed(() => sqlUi?.value?.getDataTypeListForUiType(formState))
// set additional validations
setAdditionalValidations({})
// 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
</script>
<template>
<div class="p-4 border-[2px] radius-1 border-grey w-full">
<div class="flex justify-space-between">
<a-form-item label="NN">
<a-checkbox
v-model:checked="formState.rqd"
:disabled="formState.pk || !sqlUi.columnEditable(formState)"
size="small"
class="nc-column-name-input"
@change="onAlter"
/>
</a-form-item>
<a-form-item label="PK">
<a-checkbox
v-model:checked="formState.pk"
:disabled="!sqlUi.columnEditable(formState)"
size="small"
class="nc-column-name-input"
@change="onAlter"
/>
</a-form-item>
<a-form-item label="AI">
<a-checkbox
v-model:checked="formState.ai"
:disabled="sqlUi.colPropUNDisabled(formState) || !sqlUi.columnEditable(formState)"
size="small"
class="nc-column-name-input"
@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" size="small" class="nc-column-name-input" />
</a-form-item>
<a-form-item
label="AU"
:disabled="sqlUi.colPropAuDisabled(formState) || !sqlUi.columnEditable(formState)"
@change="onAlter"
>
<a-checkbox v-model:checked="formState.au" size="small" class="nc-column-name-input" />
</a-form-item>
</div>
<a-form-item :label="$t('labels.databaseType')" v-bind="validateInfos.dt">
<a-select v-model:value="formState.dt" size="small" @change="onDataTypeChange">
<a-select-option v-for="type in dataTypes" :key="type" :value="type">
{{ type }}
</a-select-option>
</a-select>
</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)"
size="small"
@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)" size="small" @input="onAlter" />
</a-form-item>
<a-form-item :label="$t('placeholder.defaultValue')">
<a-textarea
v-model="formState.cdf"
:help="sqlUi.getDefaultValueForDatatype(formState.dt)"
size="small"
auto-size
@input="onAlter(2, true)"
/>
</a-form-item>
</div>
</template>
<style scoped></style>

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

@ -0,0 +1,145 @@
<script lang="ts" setup>
import { computed, inject, watchEffect } from '#imports'
import { useColumnCreateStoreOrThrow } from '~/composables/useColumnCreateStore'
import useMetas from '~/composables/useMetas'
import { MetaInj } from '~/context'
import { uiTypes } from '~/utils/columnUtils'
import MdiPlusIcon from '~icons/mdi/plus-circle-outline'
import MdiMinusIcon from '~icons/mdi/minus-circle-outline'
const emit = defineEmits(['cancel'])
const meta = inject(MetaInj)
const advancedOptions = ref(false)
const { getMeta } = useMetas()
const {
formState,
resetFields,
validate,
validateInfos,
onUidtOrIdTypeChange,
onAlter,
addOrUpdate,
generateNewColumnMeta,
isEdit,
} = useColumnCreateStoreOrThrow()
const uiTypesOptions = computed<typeof uiTypes>(() => {
return [
...uiTypes.filter((t) => !isEdit || !t.virtual),
...(!isEdit && meta?.value?.columns?.every((c) => !c.pk)
? [
{
name: 'ID',
icon: 'mdi-identifier',
},
]
: []),
]
})
const reloadMeta = () => {
emit('cancel')
getMeta(meta?.value.id as string, true)
}
// create column meta if it's a new column
watchEffect(() => {
if (!isEdit) {
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()
}, 300)
}
})
</script>
<template>
<div class="max-w-[450px] min-w-[350px] w-max max-h-[95vh] bg-white shadow p-4 overflow-auto" @click.stop>
<a-form v-model="formState" name="column-create-or-edit" layout="vertical">
<a-form-item :label="$t('labels.columnName')" v-bind="validateInfos.column_name">
<a-input
ref="antInput"
v-model:value="formState.column_name"
size="small"
class="nc-column-name-input"
@input="onAlter(8)"
/>
</a-form-item>
<a-form-item :label="$t('labels.columnType')">
<a-select v-model:value="formState.uidt" size="small" class="nc-column-name-input" @change="onUidtOrIdTypeChange">
<a-select-option v-for="opt in uiTypesOptions" :key="opt.name" :value="opt.name" v-bind="validateInfos.uidt">
<div class="flex gap-1 align-center text-xs">
<component :is="opt.icon" class="text-grey" />
{{ opt.name }}
</div>
</a-select-option>
</a-select>
</a-form-item>
<div>
<div
class="text-xs cursor-pointer text-grey nc-more-options my-2 flex align-center gap-1 justify-end"
@click="advancedOptions = !advancedOptions"
>
{{ advancedOptions ? $t('general.hideAll') : $t('general.showMore') }}
<component :is="advancedOptions ? MdiMinusIcon : MdiPlusIcon" />
</div>
</div>
<div class="overflow-hidden" :class="advancedOptions ? 'h-min' : 'h-0'">
<SmartsheetColumnAdvancedOptions />
</div>
<a-form-item>
<div class="flex justify-end gap-1 mt-4">
<a-button html-type="button" size="small" @click="emit('cancel')">
<!-- Cancel -->
{{ $t('general.cancel') }}
</a-button>
<a-button html-type="submit" type="primary" size="small" @click="addOrUpdate(reloadMeta)">
<!-- Save -->
{{ $t('general.save') }}
</a-button>
</div>
</a-form-item>
</a-form>
</div>
</template>
<style scoped>
:deep(.ant-form-item-label > label) {
@apply !text-xs;
}
:deep(.ant-form-item-label) {
@apply !pb-0;
}
:deep(.ant-form-item-control-input) {
@apply !min-h-min;
}
:deep(.ant-form-item) {
@apply !mb-1;
}
:deep(.ant-select-selection-item) {
@apply flex align-center;
}
:deep(.ant-form-item-explain-error) {
@apply !text-[10px];
}
:deep(.ant-form-item-explain) {
@apply !min-h-[15px];
}
</style>

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

@ -1,9 +1,16 @@
<script setup lang="ts">
import type { ColumnType } from 'nocodb-sdk'
import { ColumnInj } from '../../context'
import type { ColumnType, TableType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { inject } from 'vue'
import { ColumnInj, MetaInj } from '../../context'
import { useProvideColumnCreateStore } from '~/composables/useColumnCreateStore'
const { column } = defineProps<{ column: ColumnType & { meta: any } }>()
provide(ColumnInj, column)
const meta = inject(MetaInj)
// instantiate column update store
useProvideColumnCreateStore(meta as Ref<TableType>, column)
/*
import { UITypes } from 'nocodb-sdk'
@ -70,96 +77,12 @@ export default {
</script>
<template>
<div class="d-flex align-center d-100">
<div class="flex align-center w-full">
<SmartsheetHeaderCellIcon v-if="column" />
<span v-if="column" class="name" style="white-space: nowrap" :title="column.title">{{ column.title }}</span>
<!-- <span v-if="(column.rqd && !column.cdf) || required" class="error&#45;&#45;text text&#45;&#45;lighten-1">&nbsp;*</span> -->
<v-spacer />
<!-- todo: implement delete or edit column
<v-menu
v-if="!isLocked && !isPublicView && _isUIAllowed('edit-column') && !isForm"
open-on-hover
left
z-index="999"
transition="slide-y-transition"
>
<template #activator="{ on }">
<v-icon v-if="!isLocked && !isVirtual" small v-on="on"> mdi-menu-down</v-icon>
</template>
<v-list dense>
<v-list-item class="nc-column-edit" dense @click="showColumnEdit">
<x-icon small class="mr-1" color="primary"> mdi-pencil</x-icon>
<span class="caption">
&lt;!&ndash; Edit &ndash;&gt;
{{ $t('general.edit') }}
</span>
</v-list-item>
<v-list-item v-t="['a:column:set-primary']" dense @click="setAsPrimaryValue">
<x-icon small class="mr-1" color="primary"> mdi-key-star</x-icon>
<v-tooltip bottom>
<template #activator="{ on }">
<span class="caption" v-on="on">
&lt;!&ndash; Set as Primary value &ndash;&gt;
{{ $t('activity.setPrimary') }}
</span>
</template>
<span class="caption font-weight-bold">Primary value will be shown in place of primary key</span>
</v-tooltip>
</v-list-item>
<v-list-item class="nc-column-delete" @click="columnDeleteDialog = true">
<x-icon small class="mr-1" color="error"> mdi-delete-outline</x-icon>
<span class="caption">
&lt;!&ndash; Delete &ndash;&gt;
{{ $t('general.delete') }}
</span>
</v-list-item>
</v-list>
</v-menu>
<v-menu v-model="editColumnMenu" z-index="999" offset-y content-class="" left transition="slide-y-transition">
<template #activator="{ on }">
<span v-on="on" />
</template>
<EditColumn
v-if="editColumnMenu"
:meta="meta"
:sql-ui="sqlUi"
:nodes="nodes"
:edit-column="true"
:column="column"
:column-index="columnIndex"
@onRelationDelete="$emit('onRelationDelete')"
@saved="(_cn, _cno) => $emit('saved', _cn, _cno)"
@close="editColumnMenu = false"
/>
</v-menu>
<v-dialog
v-model="columnDeleteDialog"
max-width="500"
persistent
@keydown.esc="columnDeleteDialog = false"
@keydown.enter="deleteColumn"
>
<v-card class="nc-delete-dialog-card">
<v-card-title class="grey darken-2 subheading white&#45;&#45;text"> Confirm</v-card-title>
<v-divider />
<v-card-text class="mt-4 title">
Do you want to delete <span class="font-weight-bold">'{{ column.title }}'</span> column ?
</v-card-text>
<v-divider />
<v-card-actions class="d-flex pa-4">
<v-spacer />
<v-btn small @click="columnDeleteDialog = false">
&lt;!&ndash; Cancel &ndash;&gt;
{{ $t('general.cancel') }}
</v-btn>
<v-btn v-t="['a:column:delete']" small color="error" @click="deleteColumn"> Confirm</v-btn>
</v-card-actions>
</v-card>
</v-dialog> -->
<div class="flex-1" />
<SmartsheetHeaderMenu />
</div>
</template>

42
packages/nc-gui-v2/components/smartsheet-header/Menu.vue

@ -0,0 +1,42 @@
<script lang="ts" setup>
import MdiEditIcon from '~icons/mdi/pencil'
import MdiStarIcon from '~icons/mdi/star'
import MdiDeleteIcon from '~icons/mdi/delete-outline'
import MdiMenuDownIcon from '~icons/mdi/menu-down'
const editColumnDropdown = $ref(false)
</script>
<template>
<a-dropdown v-model:visible="editColumnDropdown" :trigger="['click']">
<span />
<template #overlay>
<SmartsheetColumnEditOrAdd @click.stop @cancel="editColumnDropdown = false" />
</template>
</a-dropdown>
<a-dropdown :trigger="['hover']">
<MdiMenuDownIcon class="text-grey" />
<template #overlay>
<div class="shadow bg-white">
<div class="nc-column-edit nc-menu-item" @click="editColumnDropdown = true">
<MdiEditIcon class="text-primary" />
<!-- Edit -->
{{ $t('general.edit') }}
</div>
<div v-t="['a:column:set-primary']" class="nc-menu-item">
<MdiStarIcon class="text-primary" />
<!-- todo : tooltip -->
<!-- Set as Primary value -->
{{ $t('activity.setPrimary') }}
<!-- <span class="caption font-weight-bold">Primary value will be shown in place of primary key</span> -->
</div>
<div class="nc-column-delete nc-menu-item">
<MdiDeleteIcon class="text-error" />
<!-- Delete -->
{{ $t('general.delete') }}
</div>
</div>
</template>
</a-dropdown>
</template>

4
packages/nc-gui-v2/components/smartsheet-toolbar/FieldsMenu.vue

@ -1,4 +1,6 @@
<script setup lang="ts">
import type { TableType } from 'nocodb-sdk'
import type { ComputedRef } from 'vue'
import { computed, inject } from 'vue'
import Draggable from 'vuedraggable'
import { ActiveViewInj, FieldsInj, IsLockedInj, MetaInj, ReloadViewDataHookInj } from '~/context'
@ -38,7 +40,7 @@ const {
hideAll,
saveOrUpdate,
sortedFields,
} = useViewColumns(activeView, meta, false, () => reloadDataHook?.trigger())
} = useViewColumns(activeView, meta as ComputedRef<TableType>, false, () => reloadDataHook?.trigger())
watch(
() => activeView?.value?.id,

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

@ -1,6 +1,7 @@
<script lang="ts" setup>
import { isVirtualCol } from 'nocodb-sdk'
import { getCurrentInstance, inject, onKeyStroke, onMounted, provide } from '#imports'
import { inject, onKeyStroke, onMounted, provide } from '#imports'
import { useProvideColumnCreateStore } from '~/composables/useColumnCreateStore'
import useGridViewColumnWidth from '~/composables/useGridViewColumnWidth'
import {
ActiveViewInj,
@ -13,6 +14,7 @@ import {
ReloadViewDataHookInj,
} from '~/context'
import useViewData from '~/composables/useViewData'
import MdiPlusIcon from '~icons/mdi/plus'
const meta = inject(MetaInj)
const view = inject(ActiveViewInj)
@ -25,6 +27,7 @@ const isPublicView = false
const selected = reactive<{ row?: number | null; col?: number | null }>({})
const editEnabled = ref(false)
const addColumnDropdown = ref(false)
const { loadData, paginationData, formattedData: data, updateRowProperty, changePage } = useViewData(meta, view)
const { loadGridViewColumns, updateWidth, resizingColWidth, resizingCol } = useGridViewColumnWidth(view)
@ -72,6 +75,11 @@ const onXcResizing = (cn: string, event: any) => {
defineExpose({
loadData,
})
// instantiate column create store
// watchEffect(() => {
if (meta) useProvideColumnCreateStore(meta)
// })
</script>
<template>
@ -93,6 +101,17 @@ defineExpose({
<SmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" />
<SmartsheetHeaderCell v-else :column="col" />
</th>
<!-- v-if="!isLocked && !isVirtual && !isPublicView && _isUIAllowed('add-column')" -->
<th v-t="['c:column:add']" @click="addColumnDropdown = true">
<a-dropdown v-model:visible="addColumnDropdown" :trigger="['click']">
<div class="h-full w-full flex align-center justify-center">
<MdiPlusIcon class="text-sm" />
</div>
<template #overlay>
<SmartsheetColumnEditOrAdd @click.stop @cancel="addColumnDropdown = false" />
</template>
</a-dropdown>
</th>
</tr>
</thead>
<tbody>
@ -219,9 +238,15 @@ defineExpose({
td,
th {
min-height: 31px !important;
min-height: 41px !important;
height: 41px !important;
position: relative;
padding: 0 5px !important;
padding: 0 5px;
& > * {
@apply flex align-center h-auto;
}
overflow: hidden;
}
table,
@ -237,7 +262,6 @@ defineExpose({
}
td {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

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

@ -0,0 +1,223 @@
import { createInjectionState } from '@vueuse/core'
import { Form } from 'ant-design-vue'
import type { ColumnType, TableType } from 'nocodb-sdk'
import { UITypes } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { useToast } from 'vue-toastification'
import { computed } from '#imports'
import { useNuxtApp } from '#app'
import useColumn from '~/composables/useColumn'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
const useForm = Form.useForm
// enum ColumnAlterType {
// NEW=4,
// EDIT=2,
// RENAME=8,
// DELETE=0,
// }
const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber]
const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState((meta: Ref<TableType>, column?: ColumnType) => {
const { sqlUi } = useProject()
const { $api } = useNuxtApp()
const toast = useToast()
const idType = null
// state
// todo: give proper type - ColumnType
const formState = ref<Partial<Record<string, any>>>({
title: 'title',
uidt: UITypes.SingleLineText,
...(column || {}),
})
const additionalValidations = ref<Record<string, any>>({})
const validators = computed(() => {
return {
column_name: [
{
required: true,
message: 'Column name is required',
},
// validation for unique column name
{
validator: (rule: any, value: any) => {
return new Promise<void>((resolve, reject) => {
if (
meta.value?.columns?.some(
(c) =>
c.id !== formState.value.id && // ignore current column
// compare against column_name and title
((value || '').toLowerCase() === (c.column_name || '').toLowerCase() ||
(value || '').toLowerCase() === (c.title || '').toLowerCase()),
)
) {
return reject(new Error('Duplicate column name'))
}
resolve()
})
},
},
],
uidt: [
{
required: true,
message: 'UI Datatype is required',
},
],
...(additionalValidations?.value || {}),
}
})
const { resetFields, validate, validateInfos } = useForm(formState, validators)
// actions
const generateNewColumnMeta = () => {
formState.value = sqlUi.value.getNewColumn((meta.value.columns?.length || 0) + 1)
}
const setAdditionalValidations = (validations: Record<string, any>) => {
additionalValidations.value = validations
}
const onUidtOrIdTypeChange = () => {
const { isCurrency } = useColumn(formState.value as ColumnType)
const colProp = sqlUi?.value.getDataTypeForUiType(formState?.value as any, idType as any)
formState.value = {
...formState.value,
meta: null,
rqd: false,
pk: false,
ai: false,
cdf: null,
un: false,
dtx: 'specificType',
...colProp,
}
formState.value.dtxp = sqlUi.value.getDefaultLengthForDatatype(formState.value.dt)
formState.value.dtxs = sqlUi.value.getDefaultScaleForDatatype(formState.value.dt)
const selectTypes = [UITypes.MultiSelect, UITypes.SingleSelect]
if (column && selectTypes.includes(formState.value.uidt) && selectTypes.includes(column.uidt as UITypes)) {
formState.value.dtxp = column.dtxp
}
if (columnToValidate.includes(formState.value.uidt)) {
formState.value.meta = {
validate: formState.value.meta && formState.value.meta.validate,
}
}
if (isCurrency) {
if (column?.uidt === UITypes.Currency) {
formState.value.dtxp = column.dtxp
formState.value.dtxs = column.dtxs
} else {
formState.value.dtxp = 19
formState.value.dtxs = 2
}
}
formState.value.altered = formState.value.altered || 2
}
const onDataTypeChange = () => {
const { isCurrency } = useColumn(formState.value as ColumnType)
formState.value.rqd = false
if (formState.value.uidt !== UITypes.ID) {
formState.value.primaryKey = false
}
formState.value.ai = false
formState.value.cdf = null
formState.value.un = false
formState.value.dtxp = sqlUi.value.getDefaultLengthForDatatype(formState.value.dt)
formState.value.dtxs = sqlUi.value.getDefaultScaleForDatatype(formState.value.dt)
formState.value.dtx = 'specificType'
const selectTypes = [UITypes.MultiSelect, UITypes.SingleSelect]
if (column && selectTypes.includes(formState.value.uidt) && selectTypes.includes(column.uidt as UITypes)) {
formState.value.dtxp = column.dtxp
}
if (isCurrency) {
if (column?.uidt === UITypes.Currency) {
formState.value.dtxp = column.dtxp
formState.value.dtxs = column.dtxs
} else {
formState.value.dtxp = 19
formState.value.dtxs = 2
}
}
// this.$set(formState.value, 'uidt', sqlUi.value.getUIType(formState.value));
formState.value.altered = formState.value.altered || 2
}
const onAlter = (val = 2, cdf = false) => {
formState.value.altered = formState.value.altered || val
if (cdf) formState.value.cdf = formState.value.cdf || null
}
const addOrUpdate = async (onSuccess: () => {}) => {
if (!(await validate())) return
formState.value.table_name = meta.value.table_name
formState.value.title = formState.value.column_name
try {
if (column) {
await $api.dbTableColumn.update(column.id as string, formState.value)
toast.success('Column updated')
} else {
// todo : set additional meta for auto generated string id
if (formState.value.uidt === UITypes.ID) {
// based on id column type set autogenerated meta prop
// if (isAutoGenId) {
// this.newColumn.meta = {
// ag: 'nc',
// };
// }
}
await $api.dbTableColumn.create(meta.value.id as string, formState.value)
toast.success('Column created')
}
onSuccess()
} catch (e) {
toast.error(await extractSdkResponseErrorMsg(e))
}
}
return {
formState,
resetFields,
validate,
validateInfos,
setAdditionalValidations,
onUidtOrIdTypeChange,
sqlUi,
onDataTypeChange,
onAlter,
addOrUpdate,
generateNewColumnMeta,
isEdit: !!column?.id,
}
})
export { useProvideColumnCreateStore }
export function useColumnCreateStoreOrThrow() {
const columnCreateStore = useColumnCreateStore()
if (columnCreateStore == null) throw new Error('Please call `useColumnCreateStore` on the appropriate parent component')
return columnCreateStore
}

2
packages/nc-gui-v2/composables/useGlobalState/index.ts

@ -1,8 +1,8 @@
import { breakpointsTailwind, usePreferredLanguages, useStorage } from '@vueuse/core'
import { useJwt } from '@vueuse/integrations/useJwt'
import type { JwtPayload } from 'jwt-decode'
import initialFeedBackForm from './initialFeedbackForm'
import { notification } from 'ant-design-vue'
import initialFeedBackForm from './initialFeedbackForm'
import { computed, ref, toRefs, useBreakpoints, useNuxtApp, useTimestamp, watch } from '#imports'
import type { Actions, Getters, GlobalState, StoredState, User } from '~/lib/types'

2
packages/nc-gui-v2/composables/useGridViewColumnWidth.ts

@ -5,7 +5,7 @@ import useMetas from '~/composables/useMetas'
import useUIPermission from '~/composables/useUIPermission'
// todo: update swagger
export default (view: Ref<(GridType & { id?: string }) | undefined>) => {
export default (view: Ref<GridType & { id?: string }>) => {
const { css, load: loadCss, unload: unloadCss } = useStyleTag('')
const { isUIAllowed } = useUIPermission()
const { $api } = useNuxtApp()

7
packages/nc-gui-v2/composables/useProject.ts

@ -1,5 +1,5 @@
import { SqlUiFactory } from 'nocodb-sdk'
import type { ProjectType, TableType } from 'nocodb-sdk'
import type { OracleUi, ProjectType, TableType } from 'nocodb-sdk'
import type { MaybeRef } from '@vueuse/core'
import { useNuxtApp, useState } from '#app'
import { USER_PROJECT_ROLES } from '~/lib/constants'
@ -21,6 +21,7 @@ export default (projectId?: MaybeRef<string>) => {
projectRoles.value = user.roles
}
}
async function loadTables() {
if (project.value.id) {
const tablesResponse = await $api.dbTable.list(project.value.id)
@ -44,7 +45,9 @@ export default (projectId?: MaybeRef<string>) => {
const isMysql = computed(() => ['mysql', 'mysql2'].includes(projectBaseType))
const isPg = computed(() => projectBaseType === 'pg')
const sqlUi = computed(() => SqlUiFactory.create({ client: projectBaseType }))
const sqlUi = computed(
() => SqlUiFactory.create({ client: projectBaseType }) as Exclude<ReturnType<typeof SqlUiFactory['create']>, typeof OracleUi>,
)
return { project, tables, loadProjectRoles, loadProject, loadTables, isMysql, isPg, sqlUi }
}

8
packages/nc-gui-v2/composables/useViewColumns.ts

@ -1,11 +1,12 @@
import { isSystemColumn } from 'nocodb-sdk'
import type { ColumnType, FormType, GalleryType, GridType, TableType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { watch } from 'vue'
import type { ComputedRef, Ref } from 'vue'
import { useNuxtApp } from '#app'
export default function (
view: Ref<(GridType | FormType | GalleryType) & { id?: string }> | undefined,
meta: Ref<TableType> | undefined,
meta: ComputedRef<TableType>,
isPublic = false,
reloadData?: () => void,
) {
@ -129,6 +130,9 @@ export default function (
[]) as ColumnType[]
})
// reload view columns when table meta changes
watch(meta, () => loadViewColumns())
return {
fields,
loadViewColumns,

2
packages/nc-gui-v2/context/index.ts

@ -5,7 +5,7 @@ import type { useViewData } from '#imports'
import type { TabItem } from '~/composables/useTabs'
export const ColumnInj: InjectionKey<ColumnType & { meta: any }> = Symbol('column-injection')
export const MetaInj: InjectionKey<Ref<TableType>> = Symbol('meta-injection')
export const MetaInj: InjectionKey<ComputedRef<TableType>> = Symbol('meta-injection')
export const TabMetaInj: InjectionKey<ComputedRef<TabItem>> = Symbol('tab-meta-injection')
export const PaginationDataInj: InjectionKey<ReturnType<typeof useViewData>['paginationData']> =
Symbol('pagination-data-injection')

189
packages/nc-gui-v2/utils/columnUtils.ts

@ -0,0 +1,189 @@
import { UITypes } from 'nocodb-sdk'
import LinkVariant from '~icons/mdi/link-variant'
import TableColumnPlusBefore from '~icons/mdi/table-column-plus-before'
import FormatColorText from '~icons/mdi/format-color-text'
import TextSubject from '~icons/mdi/text-subject'
import Attachment from '~icons/mdi/attachment'
import CheckboxMarkedOutline from '~icons/mdi/checkbox-marked-outline'
import FormatListBulletedSquare from '~icons/mdi/format-list-bulleted-square'
import ArrowDownDropCircle from '~icons/mdi/arrow-down-drop-circle'
import CalendarMonth from '~icons/mdi/calendar-month'
import Calendar from '~icons/mdi/calendar'
import Clock from '~icons/mdi/clock'
import FilePhone from '~icons/mdi/file-phone'
import Email from '~icons/mdi/email'
import Web from '~icons/mdi/web'
import Numeric from '~icons/mdi/numeric'
import Decimal from '~icons/mdi/decimal'
import CurrencyUsdCircleOutline from '~icons/mdi/currency-usd-circle-outline'
import PercentOutline from '~icons/mdi/percent-outline'
import TimerOutline from '~icons/mdi/timer-outline'
import Star from '~icons/mdi/star'
import MathIntegral from '~icons/mdi/math-integral'
import MovieRoll from '~icons/mdi/movie-roll'
import Counter from '~icons/mdi/counter'
import CalendarClock from '~icons/mdi/calendar-clock'
const uiTypes = [
{
name: UITypes.LinkToAnotherRecord,
icon: LinkVariant,
virtual: 1,
},
{
name: UITypes.Lookup,
icon: TableColumnPlusBefore,
virtual: 1,
},
{
name: UITypes.SingleLineText,
icon: FormatColorText,
},
{
name: UITypes.LongText,
icon: TextSubject,
},
{
name: UITypes.Attachment,
icon: Attachment,
},
{
name: UITypes.Checkbox,
icon: CheckboxMarkedOutline,
},
{
name: UITypes.MultiSelect,
icon: FormatListBulletedSquare,
},
{
name: UITypes.SingleSelect,
icon: ArrowDownDropCircle,
},
{
name: UITypes.Date,
icon: CalendarMonth,
},
{
name: UITypes.Year,
icon: Calendar,
},
{
name: UITypes.Time,
icon: Clock,
},
{
name: UITypes.PhoneNumber,
icon: FilePhone,
},
{
name: UITypes.Email,
icon: Email,
},
{
name: UITypes.URL,
icon: Web,
},
{
name: UITypes.Number,
icon: Numeric,
},
{
name: UITypes.Decimal,
icon: Decimal,
},
{
name: UITypes.Currency,
icon: CurrencyUsdCircleOutline,
},
{
name: UITypes.Percent,
icon: PercentOutline,
},
{
name: UITypes.Duration,
icon: TimerOutline,
},
{
name: UITypes.Rating,
icon: Star,
},
{
name: UITypes.Formula,
icon: MathIntegral,
virtual: 1,
},
{
name: UITypes.Rollup,
icon: MovieRoll,
virtual: 1,
},
{
name: UITypes.Count,
icon: Counter,
},
{
name: UITypes.DateTime,
icon: CalendarClock,
},
{
name: UITypes.AutoNumber,
icon: Numeric,
},
{
name: UITypes.Geometry,
icon: 'mdi-ruler-square-compass',
},
{
name: UITypes.JSON,
icon: 'mdi-code-json',
},
{
name: UITypes.SpecificDBType,
icon: 'mdi-database-settings',
},
]
const getUIDTIcon = (uidt: UITypes) => {
return (
[
...uiTypes,
{
name: UITypes.CreateTime,
icon: 'mdi-calendar-clock',
},
{
name: UITypes.ID,
icon: 'mdi-identifier',
},
{
name: UITypes.ForeignKey,
icon: 'mdi-link-variant',
},
].find((t) => t.name === uidt) || {}
).icon
}
export { uiTypes, getUIDTIcon }
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

2
packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts

@ -1189,7 +1189,7 @@ export class MssqlUi {
return colProp;
}
static getDataTypeListForUiType(col, idType: IDType) {
static getDataTypeListForUiType(col, idType?: IDType) {
switch (col.uidt) {
case 'ID':
if (idType === 'AG') {

2
packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts

@ -1081,7 +1081,7 @@ export class MysqlUi {
return colProp;
}
static getDataTypeListForUiType(col, idType: IDType) {
static getDataTypeListForUiType(col, idType?: IDType) {
switch (col.uidt) {
case 'ID':
if (idType === 'AG') {

2
packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts

@ -1705,7 +1705,7 @@ export class PgUi {
return colProp;
}
static getDataTypeListForUiType(col: { uidt: UITypes }, idType: IDType) {
static getDataTypeListForUiType(col: { uidt?: UITypes }, idType: IDType) {
switch (col.uidt) {
case 'ID':
if (idType === 'AG') {

2
packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts

@ -898,7 +898,7 @@ export class SqliteUi {
return colProp;
}
static getDataTypeListForUiType(col: { uidt: UITypes }, idType: IDType) {
static getDataTypeListForUiType(col: { uidt: UITypes }, idType?: IDType) {
switch (col.uidt) {
case 'ID':
if (idType === 'AG') {

Loading…
Cancel
Save