From 19c38753a4dadfa2ea4e1dffda1c04294fbfa752 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Mon, 26 Sep 2022 09:48:03 +0200 Subject: [PATCH 01/18] refactor(nc-gui): reduce computed values generated by cells --- .../nc-gui/components/dashboard/TreeView.vue | 1 + .../nc-gui/components/smartsheet/Cell.vue | 136 ++++++++++-------- .../components/smartsheet/VirtualCell.vue | 10 +- .../nc-gui/components/virtual-cell/Lookup.vue | 5 +- packages/nc-gui/composables/useColumn.ts | 131 ++++++----------- packages/nc-gui/composables/useVirtualCell.ts | 50 +++---- packages/nc-gui/utils/cell.ts | 59 ++++++++ .../nc-gui/utils/parsers/parserHelpers.ts | 4 +- packages/nc-gui/utils/validation.ts | 2 +- packages/nc-gui/utils/virtualCell.ts | 16 +++ 10 files changed, 217 insertions(+), 197 deletions(-) create mode 100644 packages/nc-gui/utils/cell.ts create mode 100644 packages/nc-gui/utils/virtualCell.ts diff --git a/packages/nc-gui/components/dashboard/TreeView.vue b/packages/nc-gui/components/dashboard/TreeView.vue index 1d6d5ea031..0302a0f553 100644 --- a/packages/nc-gui/components/dashboard/TreeView.vue +++ b/packages/nc-gui/components/dashboard/TreeView.vue @@ -336,6 +336,7 @@ const onSearchCloseIconClick = () => { > +
import type { ColumnType } from 'nocodb-sdk' +import { SqlUiFactory, isVirtualCol, UITypes } from 'nocodb-sdk' import { ActiveCellInj, ColumnInj, @@ -10,11 +11,36 @@ import { ReadonlyInj, computed, inject, + isAttachment, + isAutoSaved, + isBoolean, + isCurrency, + isDate, + isDateTime, + isDecimal, + isDuration, + isEmail, + isFloat, + isInt, + isJSON, + isManualSaved, + isMultiSelect, + isPercent, + isPhoneNumber, + isPrimary, + isRating, + isSingleSelect, + isString, + isTextArea, + isTime, + isURL, + isYear, + isPrimaryKey, provide, ref, toRef, - useColumn, useDebounceFn, + useProject, useSmartsheetRowStoreOrThrow, useVModel, } from '#imports' @@ -46,9 +72,7 @@ provide(EditModeInj, useVModel(props, 'editEnabled', emit)) provide(ActiveCellInj, active) -if (readOnly?.value) { - provide(ReadonlyInj, readOnly) -} +provide(ReadonlyInj, readOnly) const isForm = inject(IsFormInj, ref(false)) @@ -58,41 +82,23 @@ const isLocked = inject(IsLockedInj, ref(false)) const { currentRow } = useSmartsheetRowStoreOrThrow() -const syncValue = useDebounceFn( - () => { - currentRow.value.rowMeta.changed = false - emit('save') - }, - 500, +const { project } = useProject() + +const abstractType = computed(() => { + // kludge: CY test hack; column.value is being received NULL during attach cell delete operation + return (column.value && isVirtualCol(column.value)) || !column.value + ? null + : SqlUiFactory.create( + project.value?.bases?.[0]?.type ? { client: project.value.bases[0].type } : { client: 'mysql2' }, + ).getAbstractType(column.value) +}) + +const syncValue = useDebounceFn(() => { + currentRow.value.rowMeta.changed = false + emit('save') +}, 500, { maxWait: 2000 }, ) -const { - isPrimary, - isURL, - isEmail, - isJSON, - isDate, - isYear, - isDateTime, - isTime, - isBoolean, - isDuration, - isRating, - isCurrency, - isAttachment, - isTextArea, - isString, - isInt, - isFloat, - isDecimal, - isSingleSelect, - isMultiSelect, - isPercent, - isPhoneNumber, - isAutoSaved, - isManualSaved, - isPrimaryKey, -} = useColumn(column) const vModel = computed({ get: () => props.modelValue, @@ -100,9 +106,9 @@ const vModel = computed({ if (val !== props.modelValue) { currentRow.value.rowMeta.changed = true emit('update:modelValue', val) - if (isAutoSaved.value) { + if (isAutoSaved(column.value)) { syncValue() - } else if (!isManualSaved.value) { + } else if (!isManualSaved(column.value)) { emit('save') currentRow.value.rowMeta.changed = true } @@ -112,7 +118,7 @@ const vModel = computed({ const syncAndNavigate = (dir: NavigateDir, e: KeyboardEvent) => { console.log('syncAndNavigate', e.target) - if (isJSON.value) return + if (isJSON(column.value)) return if (currentRow.value.rowMeta.changed || currentRow.value.rowMeta.new) { emit('save') @@ -127,32 +133,36 @@ const syncAndNavigate = (dir: NavigateDir, e: KeyboardEvent) => { diff --git a/packages/nc-gui/components/smartsheet/VirtualCell.vue b/packages/nc-gui/components/smartsheet/VirtualCell.vue index 9e935f9550..401753ba6e 100644 --- a/packages/nc-gui/components/smartsheet/VirtualCell.vue +++ b/packages/nc-gui/components/smartsheet/VirtualCell.vue @@ -24,7 +24,7 @@ provide(CellValueInj, toRef(props, 'modelValue')) const isForm = inject(IsFormInj, ref(false)) -const { isLookup, isBt, isRollup, isMm, isHm, isFormula, isCount } = useVirtualCell(column) +const virtualCell = useVirtualCell(column) function onNavigate(dir: NavigateDir, e: KeyboardEvent) { emit('navigate', dir) @@ -39,12 +39,6 @@ function onNavigate(dir: NavigateDir, e: KeyboardEvent) { @keydown.enter.exact="onNavigate(NavigateDir.NEXT, $event)" @keydown.shift.enter.exact="onNavigate(NavigateDir.PREV, $event)" > - - - - - - - +
diff --git a/packages/nc-gui/components/virtual-cell/Lookup.vue b/packages/nc-gui/components/virtual-cell/Lookup.vue index 942a0879db..4fb41336ac 100644 --- a/packages/nc-gui/components/virtual-cell/Lookup.vue +++ b/packages/nc-gui/components/virtual-cell/Lookup.vue @@ -12,7 +12,6 @@ import { inject, provide, refAutoReset, - useColumn, useMetas, } from '#imports' @@ -46,8 +45,6 @@ const lookupColumn = computed( provide(MetaInj, lookupTableMeta) provide(CellUrlDisableOverlayInj, ref(true)) -const lookupColumnMetaProps = useColumn(lookupColumn) - const timeout = 3000 // in ms const showEditWarning = refAutoReset(false, timeout) @@ -94,7 +91,7 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e: KeyboardEven :key="i" class="min-w-max" :class="{ - 'bg-gray-100 px-1 rounded-full flex-1': !lookupColumnMetaProps.isAttachment, + 'bg-gray-100 px-1 rounded-full flex-1': !isAttachment(lookupColumn), ' border-gray-200 rounded border-1': ![UITypes.Attachment, UITypes.MultiSelect, UITypes.SingleSelect].includes( lookupColumn.uidt, ), diff --git a/packages/nc-gui/composables/useColumn.ts b/packages/nc-gui/composables/useColumn.ts index 5ba6a8b6e7..85bac800e3 100644 --- a/packages/nc-gui/composables/useColumn.ts +++ b/packages/nc-gui/composables/useColumn.ts @@ -1,98 +1,53 @@ import type { ColumnType } from 'nocodb-sdk' import { SqlUiFactory, UITypes, isVirtualCol } from 'nocodb-sdk' import type { ComputedRef, Ref } from 'vue' -import { computed, useProject } from '#imports' +import { computed, dataTypeLow, isBoolean, isPrimary, isString, isTextArea, useProject } from '#imports' export function useColumn(column: Ref) { const { project } = useProject() - const uiDatatype: ComputedRef = computed(() => column.value?.uidt as UITypes) + const data = computed(() => { + if (!column.value) return null - const abstractType = computed(() => { - // kludge: CY test hack; column.value is being received NULL during attach cell delete operation - return (column.value && isVirtualCol(column.value)) || !column.value - ? null - : SqlUiFactory.create( - project.value?.bases?.[0]?.type ? { client: project.value.bases[0].type } : { client: 'mysql2' }, - ).getAbstractType(column.value) - }) - - const dataTypeLow = computed(() => column.value?.dt?.toLowerCase()) - const isBoolean = computed(() => abstractType.value === 'boolean') - const isString = computed(() => uiDatatype.value === UITypes.SingleLineText || abstractType.value === 'string') - const isTextArea = computed(() => uiDatatype.value === UITypes.LongText) - const isInt = computed(() => abstractType.value === 'integer') - const isFloat = computed(() => abstractType.value === 'float' || abstractType.value === UITypes.Number) - const isDate = computed(() => abstractType.value === 'date' || uiDatatype.value === UITypes.Date) - const isYear = computed(() => abstractType.value === 'year' || uiDatatype.value === UITypes.Year) - const isTime = computed(() => abstractType.value === 'time' || uiDatatype.value === UITypes.Time) - const isDateTime = computed(() => abstractType.value === 'datetime' || uiDatatype.value === UITypes.DateTime) - const isJSON = computed(() => uiDatatype.value === UITypes.JSON) - const isEnum = computed(() => uiDatatype.value === UITypes.SingleSelect) - const isSingleSelect = computed(() => uiDatatype.value === UITypes.SingleSelect) - const isSet = computed(() => uiDatatype.value === UITypes.MultiSelect) - const isMultiSelect = computed(() => uiDatatype.value === UITypes.MultiSelect) - const isURL = computed(() => uiDatatype.value === UITypes.URL) - const isEmail = computed(() => uiDatatype.value === UITypes.Email) - const isAttachment = computed(() => uiDatatype.value === UITypes.Attachment) - const isRating = computed(() => uiDatatype.value === UITypes.Rating) - const isCurrency = computed(() => uiDatatype.value === UITypes.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 isSpecificDBType = computed(() => uiDatatype.value === UITypes.SpecificDBType) - 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, - UITypes.Duration, - ].includes(uiDatatype.value), - ) - const isManualSaved = computed(() => [UITypes.Currency].includes(uiDatatype.value)) - const isPrimary = computed(() => column.value?.pv) - const isPrimaryKey = computed(() => !!column.value?.pk) + const abstractType = computed(() => { + // kludge: CY test hack; column.value is being received NULL during attach cell delete operation + return (column.value && isVirtualCol(column.value)) || !column.value + ? null + : SqlUiFactory.create( + project.value?.bases?.[0]?.type ? { client: project.value.bases[0].type } : { client: 'mysql2' }, + ).getAbstractType(column.value) + }) - return { - abstractType, - dataTypeLow, - isPrimary, - isBoolean, - isString, - isTextArea, - isInt, - isFloat, - isDate, - isYear, - isTime, - isDateTime, - isJSON, - isEnum, - isSet, - isURL, - isEmail, - isAttachment, - isRating, - isCurrency, - isDecimal, - isDuration, - isAutoSaved, - isManualSaved, - isSingleSelect, - isMultiSelect, - isPercent, - isPhoneNumber, - isSpecificDBType, - isPrimaryKey, - } + return { + abstractType, + dataTypeLow: dataTypeLow(column.value), + isPrimary: isPrimary(column.value), + isBoolean: isBoolean(abstractType.value), + isString: isString(column.value, abstractType.value), + isTextArea: isTextArea(column.value), + isInt, + isFloat, + isDate, + isYear, + isTime, + isDateTime, + isJSON, + isEnum, + isSet, + isURL, + isEmail, + isAttachment, + isRating, + isCurrency, + isDecimal, + isDuration, + isAutoSaved, + isManualSaved, + isSingleSelect, + isMultiSelect, + isPercent, + isPhoneNumber, + isSpecificDBType, + } + }) } diff --git a/packages/nc-gui/composables/useVirtualCell.ts b/packages/nc-gui/composables/useVirtualCell.ts index 517cd63904..bc802dee28 100644 --- a/packages/nc-gui/composables/useVirtualCell.ts +++ b/packages/nc-gui/composables/useVirtualCell.ts @@ -1,36 +1,24 @@ -import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk' -import { RelationTypes, UITypes } from 'nocodb-sdk' +import type { ColumnType } from 'nocodb-sdk' import type { Ref } from 'vue' -import { computed } from '#imports' +import { computed, isBt, isCount, isFormula, isHm, isLookup, isMm, isRollup } from '#imports' +import HasMany from '~/components/virtual-cell/HasMany.vue' +import ManyToMany from '~/components/virtual-cell/ManyToMany.vue' +import BelongsTo from '~/components/virtual-cell/BelongsTo.vue' +import Lookup from '~/components/virtual-cell/Lookup.vue' +import Rollup from '~/components/virtual-cell/Rollup.vue' +import Formula from '~/components/virtual-cell/Formula.vue' +import Count from '~/components/virtual-cell/Count.vue' export function useVirtualCell(column: Ref) { - const isHm = computed( - () => - column.value?.uidt === UITypes.LinkToAnotherRecord && - (column.value?.colOptions).type === RelationTypes.HAS_MANY, - ) - const isMm = computed( - () => - column.value?.uidt === UITypes.LinkToAnotherRecord && - (column.value?.colOptions).type === RelationTypes.MANY_TO_MANY, - ) - const isBt = computed( - () => - column.value?.uidt === UITypes.LinkToAnotherRecord && - (column.value?.colOptions).type === RelationTypes.BELONGS_TO, - ) - const isLookup = computed(() => column.value?.uidt === UITypes.Lookup) - const isRollup = computed(() => column.value?.uidt === UITypes.Rollup) - const isFormula = computed(() => column.value?.uidt === UITypes.Formula) - const isCount = computed(() => column.value?.uidt === UITypes.Count) + return computed(() => { + if (!column.value) return null - return { - isHm, - isMm, - isBt, - isLookup, - isRollup, - isFormula, - isCount, - } + if (isHm(column.value)) return HasMany + if (isMm(column.value)) return ManyToMany + if (isBt(column.value)) return BelongsTo + if (isLookup(column.value)) return Lookup + if (isRollup(column.value)) return Rollup + if (isFormula(column.value)) return Formula + if (isCount(column.value)) return Count + }) } diff --git a/packages/nc-gui/utils/cell.ts b/packages/nc-gui/utils/cell.ts new file mode 100644 index 0000000000..9051e94f7a --- /dev/null +++ b/packages/nc-gui/utils/cell.ts @@ -0,0 +1,59 @@ +import type { ColumnType, ProjectType } from 'nocodb-sdk' +import { SqlUiFactory, UITypes, isVirtualCol } from 'nocodb-sdk' + +export const abstractType = (column: ColumnType, project: ProjectType) => { + // kludge: CY test hack; column.value is being received NULL during attach cell delete operation + return (column && isVirtualCol(column)) || !column + ? null + : SqlUiFactory.create(project.bases?.[0]?.type ? { client: project.bases[0].type } : { client: 'mysql2' }).getAbstractType( + column, + ) +} + +export const dataTypeLow = (column: ColumnType) => column.dt?.toLowerCase() +export const isBoolean = (abstractType: any) => abstractType === 'boolean' +export const isString = (column: ColumnType, abstractType: any) => + column.uidt === UITypes.SingleLineText || abstractType === 'string' +export const isTextArea = (column: ColumnType) => column.uidt === UITypes.LongText +export const isInt = (column: ColumnType, abstractType: any) => abstractType === 'integer' +export const isFloat = (column: ColumnType, abstractType: any) => abstractType === 'float' || abstractType === UITypes.Number +export const isDate = (column: ColumnType, abstractType: any) => abstractType === 'date' || column.uidt === UITypes.Date +export const isYear = (column: ColumnType, abstractType: any) => abstractType === 'year' || column.uidt === UITypes.Year +export const isTime = (column: ColumnType, abstractType: any) => abstractType === 'time' || column.uidt === UITypes.Time +export const isDateTime = (column: ColumnType, abstractType: any) => + abstractType === 'datetime' || column.uidt === UITypes.DateTime +export const isJSON = (column: ColumnType) => column.uidt === UITypes.JSON +export const isEnum = (column: ColumnType) => column.uidt === UITypes.SingleSelect +export const isSingleSelect = (column: ColumnType) => column.uidt === UITypes.SingleSelect +export const isSet = (column: ColumnType) => column.uidt === UITypes.MultiSelect +export const isMultiSelect = (column: ColumnType) => column.uidt === UITypes.MultiSelect +export const isURL = (column: ColumnType) => column.uidt === UITypes.URL +export const isEmail = (column: ColumnType) => column.uidt === UITypes.Email +export const isAttachment = (column: ColumnType) => column.uidt === UITypes.Attachment +export const isRating = (column: ColumnType) => column.uidt === UITypes.Rating +export const isCurrency = (column: ColumnType) => column.uidt === UITypes.Currency +export const isPhoneNumber = (column: ColumnType) => column.uidt === UITypes.PhoneNumber +export const isDecimal = (column: ColumnType) => column.uidt === UITypes.Decimal +export const isDuration = (column: ColumnType) => column.uidt === UITypes.Duration +export const isPercent = (column: ColumnType) => column.uidt === UITypes.Percent +export const isSpecificDBType = (column: ColumnType) => column.uidt === UITypes.SpecificDBType +export const isAutoSaved = (column: ColumnType) => + [ + 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(column.uidt as UITypes) + +export const isManualSaved = (column: ColumnType) => + [UITypes.Currency, UITypes.Year, UITypes.Time, UITypes.Duration].includes(column.uidt as UITypes) + +export const isPrimary = (column: ColumnType) => column.pv diff --git a/packages/nc-gui/utils/parsers/parserHelpers.ts b/packages/nc-gui/utils/parsers/parserHelpers.ts index 1107a3bff0..7df86117c0 100644 --- a/packages/nc-gui/utils/parsers/parserHelpers.ts +++ b/packages/nc-gui/utils/parsers/parserHelpers.ts @@ -1,6 +1,6 @@ import { UITypes } from 'nocodb-sdk' import { isValidURL } from '~/utils/urlUtils' -import { isEmail } from '~/utils/validation' +import { validateEmail } from '~/utils/validation' const booleanOptions = [ { checked: true, unchecked: false }, @@ -109,7 +109,7 @@ export const isDecimalType = (colData: []) => export const isEmailType = (colData: []) => colData.some((v: any) => { - return v && isEmail(v) + return v && validateEmail(v) }) export const isUrlType = (colData: []) => diff --git a/packages/nc-gui/utils/validation.ts b/packages/nc-gui/utils/validation.ts index 80f1b2b73e..3524a1f928 100644 --- a/packages/nc-gui/utils/validation.ts +++ b/packages/nc-gui/utils/validation.ts @@ -1,6 +1,6 @@ import { getI18n } from '~/plugins/a.i18n' -export const isEmail = (v: string) => +export const validateEmail = (v: string) => /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i.test(v) export const validateTableName = { diff --git a/packages/nc-gui/utils/virtualCell.ts b/packages/nc-gui/utils/virtualCell.ts new file mode 100644 index 0000000000..46d827bfc3 --- /dev/null +++ b/packages/nc-gui/utils/virtualCell.ts @@ -0,0 +1,16 @@ +import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk' +import { RelationTypes, UITypes } from 'nocodb-sdk' + +export const isHm = (column: ColumnType) => + column.uidt === UITypes.LinkToAnotherRecord && (column.colOptions).type === RelationTypes.HAS_MANY + +export const isMm = (column: ColumnType) => + column.uidt === UITypes.LinkToAnotherRecord && (column.colOptions).type === RelationTypes.MANY_TO_MANY + +export const isBt = (column: ColumnType) => + column.uidt === UITypes.LinkToAnotherRecord && (column.colOptions).type === RelationTypes.BELONGS_TO + +export const isLookup = (column: ColumnType) => column.uidt === UITypes.Lookup +export const isRollup = (column: ColumnType) => column.uidt === UITypes.Rollup +export const isFormula = (column: ColumnType) => column.uidt === UITypes.Formula +export const isCount = (column: ColumnType) => column.uidt === UITypes.Count From 9daa6d8b4791d14afd0648ef98f4bda98055cfe4 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Mon, 26 Sep 2022 10:12:17 +0200 Subject: [PATCH 02/18] refactor(nc-gui): remove useColumn and useVirtualCell --- .../components/smartsheet/VirtualCell.vue | 37 ++++- .../components/smartsheet/header/CellIcon.ts | 140 ++++++++++++++++++ .../components/smartsheet/header/CellIcon.vue | 89 ----------- .../smartsheet/header/VirtualCell.vue | 27 ++-- .../smartsheet/header/VirtualCellIcon.vue | 10 +- .../nc-gui/components/virtual-cell/Lookup.vue | 22 ++- packages/nc-gui/composables/useColumn.ts | 53 ------- packages/nc-gui/composables/useVirtualCell.ts | 24 --- 8 files changed, 211 insertions(+), 191 deletions(-) create mode 100644 packages/nc-gui/components/smartsheet/header/CellIcon.ts delete mode 100644 packages/nc-gui/composables/useColumn.ts delete mode 100644 packages/nc-gui/composables/useVirtualCell.ts diff --git a/packages/nc-gui/components/smartsheet/VirtualCell.vue b/packages/nc-gui/components/smartsheet/VirtualCell.vue index 401753ba6e..9b5fea1571 100644 --- a/packages/nc-gui/components/smartsheet/VirtualCell.vue +++ b/packages/nc-gui/components/smartsheet/VirtualCell.vue @@ -1,8 +1,31 @@ - - diff --git a/packages/nc-gui/components/smartsheet/header/VirtualCell.vue b/packages/nc-gui/components/smartsheet/header/VirtualCell.vue index f0dc511883..329ccac8a5 100644 --- a/packages/nc-gui/components/smartsheet/header/VirtualCell.vue +++ b/packages/nc-gui/components/smartsheet/header/VirtualCell.vue @@ -7,6 +7,12 @@ import { MetaInj, computed, inject, + isBt, + isFormula, + isHm, + isLookup, + isMm, + isRollup, isVirtualColRequired, provide, ref, @@ -14,7 +20,6 @@ import { useI18n, useMetas, useUIPermission, - useVirtualCell, } from '#imports' const props = defineProps<{ column: ColumnType; hideMenu?: boolean; required?: boolean | number }>() @@ -37,14 +42,12 @@ const meta = inject(MetaInj, ref()) const isForm = inject(IsFormInj, ref(false)) -const { isLookup, isBt, isRollup, isMm, isHm, isFormula } = useVirtualCell(column) - const colOptions = $computed(() => column.value?.colOptions) const tableTile = $computed(() => meta?.value?.title) const relationColumnOptions = $computed(() => { - if (isMm.value || isHm.value || isBt.value) { + if (isMm(column.value) || isHm(column.value) || isBt(column.value)) { return column.value?.colOptions as LinkToAnotherRecordType } else if ((column?.value?.colOptions as LookupType | RollupType)?.fk_relation_column_id) { return meta?.value?.columns?.find( @@ -62,10 +65,10 @@ const relatedTableTitle = $computed(() => relatedTableMeta?.title) const childColumn = $computed(() => { if (relatedTableMeta?.columns) { - if (isRollup.value) { + if (isRollup(column.value)) { return relatedTableMeta?.columns.find((c: ColumnType) => c.id === (colOptions as RollupType).fk_rollup_column_id) } - if (isLookup.value) { + if (isLookup(column.value)) { return relatedTableMeta?.columns.find((c: ColumnType) => c.id === (colOptions as LookupType).fk_lookup_column_id) } } @@ -76,22 +79,22 @@ const tooltipMsg = computed(() => { if (!column.value) { return '' } - if (isHm.value) { + if (isHm(column.value)) { return `'${tableTile}' ${t('labels.hasMany')} '${relatedTableTitle}'` - } else if (isMm.value) { + } else if (isMm(column.value)) { return `'${tableTile}' & '${relatedTableTitle}' ${t('labels.manyToMany')}` - } else if (isBt.value) { + } else if (isBt(column.value)) { return `'${column?.value?.title}' ${t('labels.belongsTo')} '${relatedTableTitle}'` - } else if (isLookup.value) { + } else if (isLookup(column.value)) { return `'${childColumn.title}' from '${relatedTableTitle}' (${childColumn.uidt})` - } else if (isFormula.value) { + } else if (isFormula(column.value)) { const formula = substituteColumnIdWithAliasInFormula( (column.value?.colOptions as FormulaType)?.formula, meta?.value?.columns as ColumnType[], (column.value?.colOptions as any)?.formula_raw, ) return `Formula - ${formula}` - } else if (isRollup.value) { + } else if (isRollup(column.value)) { return `'${childColumn.title}' of '${relatedTableTitle}' (${childColumn.uidt})` } return '' diff --git a/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.vue b/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.vue index 5ad8dee881..dc293b4f5e 100644 --- a/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.vue +++ b/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.vue @@ -2,7 +2,7 @@ import type { ColumnType, LinkToAnotherRecordType, LookupType } from 'nocodb-sdk' import { RelationTypes, UITypes } from 'nocodb-sdk' import type { Ref } from 'vue' -import { ColumnInj, inject, ref, toRef } from '#imports' +import { ColumnInj, MetaInj, computed, inject, isBt, isHm, isLookup, isMm, isRollup, ref, toRef } from '#imports' import GenericIcon from '~icons/mdi/square-rounded' import HMIcon from '~icons/mdi/table-arrow-right' import BTIcon from '~icons/mdi/table-arrow-left' @@ -22,9 +22,7 @@ const column = inject(ColumnInj, ref(columnMeta)) as Ref) - - if (isLookup || isBt || isRollup || isMm || isHm) { + if (isLookup(column.value) || isBt(column.value) || isRollup(column.value) || isMm(column.value) || isHm(column.value)) { const meta = inject(MetaInj, ref()) relationColumn = meta.value?.columns?.find((c) => c.id === column.value?.colOptions?.fk_relation_column_id) as ColumnType & { @@ -34,9 +32,9 @@ if (column) { } const icon = computed(() => { - switch (column?.value?.uidt) { + switch (column.value?.uidt) { case UITypes.LinkToAnotherRecord: - switch ((column?.value?.colOptions as LinkToAnotherRecordType)?.type) { + switch ((column.value?.colOptions as LinkToAnotherRecordType)?.type) { case RelationTypes.MANY_TO_MANY: return { icon: MMIcon, color: 'text-accent' } case RelationTypes.HAS_MANY: diff --git a/packages/nc-gui/components/virtual-cell/Lookup.vue b/packages/nc-gui/components/virtual-cell/Lookup.vue index 4fb41336ac..436beb6687 100644 --- a/packages/nc-gui/components/virtual-cell/Lookup.vue +++ b/packages/nc-gui/components/virtual-cell/Lookup.vue @@ -10,9 +10,12 @@ import { ReadonlyInj, computed, inject, + isAttachment, provide, refAutoReset, + ref, useMetas, + watch, } from '#imports' const { metas, getMeta } = useMetas() @@ -27,13 +30,22 @@ const value = inject(CellValueInj) const arrValue = computed(() => (Array.isArray(value?.value) ? value?.value : [value?.value]) ?? []) -const relationColumn = meta.value?.columns?.find((c) => c.id === column.value.colOptions?.fk_relation_column_id) as ColumnType & { - colOptions: LinkToAnotherRecordType -} +const relationColumn = computed( + () => + meta.value?.columns?.find((c) => c.id === column.value.colOptions?.fk_relation_column_id) as ColumnType & { + colOptions: LinkToAnotherRecordType + }, +) -await getMeta(relationColumn.colOptions.fk_related_model_id!) +watch( + relationColumn, + async () => { + await getMeta(relationColumn.value.colOptions.fk_related_model_id!) + }, + { immediate: true }, +) -const lookupTableMeta = computed(() => metas.value[relationColumn.colOptions.fk_related_model_id!]) +const lookupTableMeta = computed(() => metas.value[relationColumn.value.colOptions.fk_related_model_id!]) const lookupColumn = computed( () => diff --git a/packages/nc-gui/composables/useColumn.ts b/packages/nc-gui/composables/useColumn.ts deleted file mode 100644 index 85bac800e3..0000000000 --- a/packages/nc-gui/composables/useColumn.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { ColumnType } from 'nocodb-sdk' -import { SqlUiFactory, UITypes, isVirtualCol } from 'nocodb-sdk' -import type { ComputedRef, Ref } from 'vue' -import { computed, dataTypeLow, isBoolean, isPrimary, isString, isTextArea, useProject } from '#imports' - -export function useColumn(column: Ref) { - const { project } = useProject() - - const data = computed(() => { - if (!column.value) return null - - const abstractType = computed(() => { - // kludge: CY test hack; column.value is being received NULL during attach cell delete operation - return (column.value && isVirtualCol(column.value)) || !column.value - ? null - : SqlUiFactory.create( - project.value?.bases?.[0]?.type ? { client: project.value.bases[0].type } : { client: 'mysql2' }, - ).getAbstractType(column.value) - }) - - return { - abstractType, - dataTypeLow: dataTypeLow(column.value), - isPrimary: isPrimary(column.value), - isBoolean: isBoolean(abstractType.value), - isString: isString(column.value, abstractType.value), - isTextArea: isTextArea(column.value), - isInt, - isFloat, - isDate, - isYear, - isTime, - isDateTime, - isJSON, - isEnum, - isSet, - isURL, - isEmail, - isAttachment, - isRating, - isCurrency, - isDecimal, - isDuration, - isAutoSaved, - isManualSaved, - isSingleSelect, - isMultiSelect, - isPercent, - isPhoneNumber, - isSpecificDBType, - } - }) -} diff --git a/packages/nc-gui/composables/useVirtualCell.ts b/packages/nc-gui/composables/useVirtualCell.ts deleted file mode 100644 index bc802dee28..0000000000 --- a/packages/nc-gui/composables/useVirtualCell.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { ColumnType } from 'nocodb-sdk' -import type { Ref } from 'vue' -import { computed, isBt, isCount, isFormula, isHm, isLookup, isMm, isRollup } from '#imports' -import HasMany from '~/components/virtual-cell/HasMany.vue' -import ManyToMany from '~/components/virtual-cell/ManyToMany.vue' -import BelongsTo from '~/components/virtual-cell/BelongsTo.vue' -import Lookup from '~/components/virtual-cell/Lookup.vue' -import Rollup from '~/components/virtual-cell/Rollup.vue' -import Formula from '~/components/virtual-cell/Formula.vue' -import Count from '~/components/virtual-cell/Count.vue' - -export function useVirtualCell(column: Ref) { - return computed(() => { - if (!column.value) return null - - if (isHm(column.value)) return HasMany - if (isMm(column.value)) return ManyToMany - if (isBt(column.value)) return BelongsTo - if (isLookup(column.value)) return Lookup - if (isRollup(column.value)) return Rollup - if (isFormula(column.value)) return Formula - if (isCount(column.value)) return Count - }) -} From fca55cffb209609fb787f9b96133d8764f4d4c85 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Thu, 29 Sep 2022 16:12:58 +0200 Subject: [PATCH 03/18] chore(nc-gui): remove useVirtualCell --- .../composables/useSmartsheetRowStore.ts | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/nc-gui/composables/useSmartsheetRowStore.ts b/packages/nc-gui/composables/useSmartsheetRowStore.ts index 7c710d9d1c..47a00d4661 100644 --- a/packages/nc-gui/composables/useSmartsheetRowStore.ts +++ b/packages/nc-gui/composables/useSmartsheetRowStore.ts @@ -8,6 +8,9 @@ import { deepCompare, extractPkFromRow, extractSdkResponseErrorMsg, + isBt, + isHm, + isMm, message, ref, unref, @@ -16,7 +19,6 @@ import { useMetas, useNuxtApp, useProject, - useVirtualCell, } from '#imports' import type { Row } from '~/lib' @@ -36,12 +38,11 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState( const state = ref | Record[] | null>>({}) // getters - const isNew = computed(() => unref(row).rowMeta?.new ?? false) + const isNew = computed(() => unref(row).rowMeta.new ?? false) // actions const addLTARRef = async (value: Record, column: ColumnType) => { - const { isHm, isMm, isBt } = $(useVirtualCell(ref(column))) - if (isHm || isMm) { + if (isHm(column) || isMm(column)) { if (!state.value[column.title!]) state.value[column.title!] = [] if (state.value[column.title!]!.find((ln: Record) => deepCompare(ln, value))) { @@ -50,17 +51,16 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState( } state.value[column.title!]!.push(value) - } else if (isBt) { + } else if (isBt(column)) { state.value[column.title!] = value } } // actions const removeLTARRef = async (value: Record, column: ColumnType) => { - const { isHm, isMm, isBt } = $(useVirtualCell(ref(column))) - if (isHm || isMm) { + if (isHm(column) || isMm(column)) { state.value[column.title!]?.splice(state.value[column.title!]?.indexOf(value), 1) - } else if (isBt) { + } else if (isBt(column)) { state.value[column.title!] = null } } @@ -92,13 +92,14 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState( const id = extractPkFromRow(row, metaValue?.columns as ColumnType[]) for (const column of metaValue?.columns ?? []) { if (column.uidt !== UITypes.LinkToAnotherRecord) continue - const colOptions = column?.colOptions as LinkToAnotherRecordType - const { isHm, isMm, isBt } = $(useVirtualCell(ref(column))) + const colOptions = column.colOptions as LinkToAnotherRecordType + const relatedTableMeta = metas.value?.[colOptions?.fk_related_model_id as string] - if (isHm || isMm) { + if (isHm(column) || isMm(column)) { const relatedRows = (state.value?.[column.title!] ?? []) as Record[] + for (const relatedRow of relatedRows) { await linkRecord( id, @@ -108,7 +109,7 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState( { metaValue }, ) } - } else if (isBt && state?.value?.[column.title!]) { + } else if (isBt(column) && state.value?.[column.title!]) { await linkRecord( id, extractPkFromRow(state.value?.[column.title!] as Record, relatedTableMeta.columns as ColumnType[]), From e46b5fad94bd951fedc3ba8adda5b4a3e209a6c9 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Thu, 29 Sep 2022 16:15:08 +0200 Subject: [PATCH 04/18] chore(nc-gui): replace isEmail with validateEmail --- packages/nc-gui/components/cell/Email.vue | 4 ++-- .../tabs/auth/user-management/UsersModal.vue | 4 ++-- packages/nc-gui/pages/forgot-password.vue | 4 ++-- packages/nc-gui/pages/signin.vue | 4 ++-- packages/nc-gui/pages/signup/[[token]].vue | 15 +++++++++++++-- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/nc-gui/components/cell/Email.vue b/packages/nc-gui/components/cell/Email.vue index f7738e109f..c5c20ced60 100644 --- a/packages/nc-gui/components/cell/Email.vue +++ b/packages/nc-gui/components/cell/Email.vue @@ -1,6 +1,6 @@ diff --git a/packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue b/packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue index 59984d352c..c8b509916c 100644 --- a/packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue +++ b/packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue @@ -3,7 +3,6 @@ import { Form, computed, extractSdkResponseErrorMsg, - isEmail, message, onMounted, projectRoleTagColors, @@ -14,6 +13,7 @@ import { useI18n, useNuxtApp, useProject, + validateEmail, } from '#imports' import type { User } from '~/lib' import { ProjectRole } from '~/lib' @@ -57,7 +57,7 @@ const validators = computed(() => { callback('Email is required') return } - const invalidEmails = (value || '').split(/\s*,\s*/).filter((e: string) => !isEmail(e)) + const invalidEmails = (value || '').split(/\s*,\s*/).filter((e: string) => !validateEmail(e)) if (invalidEmails.length > 0) { callback(`${invalidEmails.length > 1 ? ' Invalid emails:' : 'Invalid email:'} ${invalidEmails.join(', ')} `) } else { diff --git a/packages/nc-gui/pages/forgot-password.vue b/packages/nc-gui/pages/forgot-password.vue index 63e5226bc8..ac73a718d1 100644 --- a/packages/nc-gui/pages/forgot-password.vue +++ b/packages/nc-gui/pages/forgot-password.vue @@ -1,5 +1,5 @@ +} + +export default defineComponent({ + name: 'VirtualCellIcon', + props: { + columnMeta: { + type: Object as PropType, + required: false, + }, + }, + setup(props) { + const columnMeta = toRef(props, 'columnMeta') + + const column = inject(ColumnInj, ref(columnMeta)) as Ref + + let relationColumn: ColumnType & { colOptions: LookupType } + + watch( + column, + () => { + if (column && column.value) { + if ( + isMm(column.value) || + isHm(column.value) || + isBt(column.value) || + isLookup(column.value) || + isRollup(column.value) + ) { + const meta = inject(MetaInj, ref()) - + relationColumn = meta.value?.columns?.find( + (c) => c.id === column.value?.colOptions?.fk_relation_column_id, + ) as ColumnType & { + colOptions: LinkToAnotherRecordType + } + } + } + }, + { immediate: true }, + ) + + return () => { + if (!column.value) return null + + const { icon: Icon, color } = renderIcon(column.value, relationColumn) + + return h(Icon, { class: `${color} mx-1 !text-xs` }) + } + }, +}) From 6bab6a459c4d9c179fecaffce4f99a9f92562211 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Mon, 10 Oct 2022 11:13:14 +0200 Subject: [PATCH 10/18] refactor(nc-gui): remove watcher from virtual cell icon --- .../smartsheet/header/VirtualCellIcon.ts | 38 +++++++------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts b/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts index 850dadee6d..029543fb48 100644 --- a/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts +++ b/packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts @@ -2,7 +2,7 @@ import type { PropType } from '@vue/runtime-core' import type { ColumnType, LinkToAnotherRecordType, LookupType } from 'nocodb-sdk' import type { Ref } from 'vue' import { RelationTypes, UITypes } from 'nocodb-sdk' -import { ColumnInj, MetaInj, defineComponent, h, inject, isBt, isHm, isLookup, isMm, isRollup, ref, toRef, watch } from '#imports' +import { ColumnInj, MetaInj, defineComponent, h, inject, isBt, isHm, isLookup, isMm, isRollup, ref, toRef } from '#imports' import GenericIcon from '~icons/mdi/square-rounded' import HMIcon from '~icons/mdi/table-arrow-right' import BTIcon from '~icons/mdi/table-arrow-left' @@ -67,36 +67,24 @@ export default defineComponent({ setup(props) { const columnMeta = toRef(props, 'columnMeta') - const column = inject(ColumnInj, ref(columnMeta)) as Ref + const column = inject(ColumnInj, columnMeta) as Ref let relationColumn: ColumnType & { colOptions: LookupType } - watch( - column, - () => { - if (column && column.value) { - if ( - isMm(column.value) || - isHm(column.value) || - isBt(column.value) || - isLookup(column.value) || - isRollup(column.value) - ) { - const meta = inject(MetaInj, ref()) + return () => { + if (!column.value) return null - relationColumn = meta.value?.columns?.find( - (c) => c.id === column.value?.colOptions?.fk_relation_column_id, - ) as ColumnType & { - colOptions: LinkToAnotherRecordType - } + if (column && column.value) { + if (isMm(column.value) || isHm(column.value) || isBt(column.value) || isLookup(column.value) || isRollup(column.value)) { + const meta = inject(MetaInj, ref()) + + relationColumn = meta.value?.columns?.find( + (c) => c.id === column.value?.colOptions?.fk_relation_column_id, + ) as ColumnType & { + colOptions: LinkToAnotherRecordType } } - }, - { immediate: true }, - ) - - return () => { - if (!column.value) return null + } const { icon: Icon, color } = renderIcon(column.value, relationColumn) From 5ea1b2b6610aac599f8bad7f9d1a246e47b816cc Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Mon, 10 Oct 2022 11:15:48 +0200 Subject: [PATCH 11/18] chore(nc-gui): lint --- packages/nc-gui/components.d.ts | 2 ++ packages/nc-gui/components/smartsheet/Cell.vue | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/nc-gui/components.d.ts b/packages/nc-gui/components.d.ts index 39218772d5..37a7852487 100644 --- a/packages/nc-gui/components.d.ts +++ b/packages/nc-gui/components.d.ts @@ -95,6 +95,8 @@ declare module '@vue/runtime-core' { MaterialSymbolsDarkModeOutline: typeof import('~icons/material-symbols/dark-mode-outline')['default'] MaterialSymbolsFileCopyOutline: typeof import('~icons/material-symbols/file-copy-outline')['default'] MaterialSymbolsKeyboardReturn: typeof import('~icons/material-symbols/keyboard-return')['default'] + MaterialSymbolsKeyboardShift: typeof import('~icons/material-symbols/keyboard-shift')['default'] + MaterialSymbolsLightMode: typeof import('~icons/material-symbols/light-mode')['default'] MaterialSymbolsLightModeOutline: typeof import('~icons/material-symbols/light-mode-outline')['default'] MaterialSymbolsRocketLaunchOutline: typeof import('~icons/material-symbols/rocket-launch-outline')['default'] MaterialSymbolsSendOutline: typeof import('~icons/material-symbols/send-outline')['default'] diff --git a/packages/nc-gui/components/smartsheet/Cell.vue b/packages/nc-gui/components/smartsheet/Cell.vue index 78bb39d0e1..46bee5063b 100644 --- a/packages/nc-gui/components/smartsheet/Cell.vue +++ b/packages/nc-gui/components/smartsheet/Cell.vue @@ -125,7 +125,10 @@ const syncAndNavigate = (dir: NavigateDir, e: KeyboardEvent) => {