Browse Source

Merge pull request #8309 from nocodb/nc-fix/field-header-context-menu

Nc fix/field header context menu
pull/8319/head
Ramesh Mane 5 months ago committed by GitHub
parent
commit
4c774715cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 96
      packages/nc-gui/components/smartsheet/header/Menu.vue
  2. 62
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
  3. 18
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilterMenu.vue
  4. 75
      packages/nc-gui/components/smartsheet/toolbar/GroupByMenu.vue
  5. 3
      packages/nc-gui/composables/useSmartsheetStore.ts
  6. 71
      packages/nc-gui/composables/useViewFilters.ts
  7. 83
      packages/nc-gui/composables/useViewGroupBy.ts
  8. 3
      packages/nc-gui/lib/enums.ts

96
packages/nc-gui/components/smartsheet/header/Menu.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import type { ColumnReqType } from 'nocodb-sdk'
import { RelationTypes, UITypes, isLinksOrLTAR } from 'nocodb-sdk'
import { PlanLimitTypes, RelationTypes, UITypes, isLinksOrLTAR } from 'nocodb-sdk'
import { computed } from 'vue'
import {
ActiveViewInj,
@ -28,7 +28,7 @@ const virtual = toRef(props, 'virtual')
const isOpen = useVModel(props, 'isOpen', emit)
const { eventBus } = useSmartsheetStoreOrThrow()
const { eventBus, allFilters } = useSmartsheetStoreOrThrow()
const column = inject(ColumnInj)
@ -38,10 +38,12 @@ const meta = inject(MetaInj, ref())
const view = inject(ActiveViewInj, ref())
const { insertSort } = useViewSorts(view, () => reloadDataHook?.trigger())
const isLocked = inject(IsLockedInj)
const isPublic = inject(IsPublicInj, ref(false))
const { insertSort } = useViewSorts(view, () => reloadDataHook?.trigger())
const { $api, $e } = useNuxtApp()
const { t } = useI18n()
@ -52,6 +54,10 @@ const { addUndo, defineModelScope, defineViewScope } = useUndoRedo()
const showDeleteColumnModal = ref(false)
const { gridViewCols } = useViewColumnsOrThrow()
const { fieldsToGroupBy, groupByLimit } = useViewGroupBy(view)
const setAsDisplayValue = async () => {
try {
const currentDisplayValue = meta?.value?.columns?.find((f) => f.pv)
@ -295,6 +301,33 @@ const isDeleteAllowed = computed(() => {
const isDuplicateAllowed = computed(() => {
return column?.value && !column.value.system
})
const isFilterSupported = computed(
() =>
!!(meta.value?.columns || []).find((f) => f.id === column?.value?.id && ![UITypes.QrCode, UITypes.Barcode].includes(f.uidt)),
)
const { getPlanLimit } = useWorkspace()
const isFilterLimitExceeded = computed(
() =>
allFilters.value.filter((f) => !(f.is_group || f.status === 'delete')).length >= getPlanLimit(PlanLimitTypes.FILTER_LIMIT),
)
const isGroupedByThisField = computed(() => !!gridViewCols.value[column?.value?.id]?.group_by)
const isGroupBySupported = computed(() => !!(fieldsToGroupBy.value || []).find((f) => f.id === column?.value?.id))
const isGroupByLimitExceeded = computed(() => {
const groupBy = Object.values(gridViewCols.value).filter((c) => c.group_by)
return !(fieldsToGroupBy.value.length && fieldsToGroupBy.value.length > groupBy.length && groupBy.length < groupByLimit)
})
const filterOrGroupByThisField = (event: SmartsheetStoreEvents) => {
if (column?.value) {
eventBus.emit(event, column.value)
}
isOpen.value = false
}
</script>
<template>
@ -362,7 +395,55 @@ const isDuplicateAllowed = computed(() => {
</NcMenuItem>
</template>
<a-divider v-if="!column?.pk" class="!my-0" />
<a-divider class="!my-0" />
<NcTooltip :disabled="isFilterSupported && !isFilterLimitExceeded">
<template #title>
{{
!isFilterSupported
? "This field type doesn't support filtering"
: isFilterLimitExceeded
? 'Filter by limit exceeded'
: ''
}}
</template>
<NcMenuItem
:disabled="!isFilterSupported || isFilterLimitExceeded"
@click="filterOrGroupByThisField(SmartsheetStoreEvents.FILTER_ADD)"
>
<div v-e="['a:field:add:filter']" class="nc-column-filter nc-header-menu-item">
<component :is="iconMap.filter" class="text-gray-700" />
<!-- Filter by this field -->
Filter by this field
</div>
</NcMenuItem>
</NcTooltip>
<NcTooltip :disabled="(isGroupBySupported && !isGroupByLimitExceeded) || isGroupedByThisField || !(isEeUI && !isPublic)">
<template #title>{{
!isGroupBySupported
? "This field type doesn't support grouping"
: isGroupByLimitExceeded
? 'Group by limit exceeded'
: ''
}}</template>
<NcMenuItem
:disabled="isEeUI && !isPublic && (!isGroupBySupported || isGroupByLimitExceeded) && !isGroupedByThisField"
@click="
filterOrGroupByThisField(
isGroupedByThisField ? SmartsheetStoreEvents.GROUP_BY_REMOVE : SmartsheetStoreEvents.GROUP_BY_ADD,
)
"
>
<div v-e="['a:field:add:groupby']" class="nc-column-groupby nc-header-menu-item">
<component :is="iconMap.group" class="text-gray-700" />
<!-- Group by this field -->
{{ isGroupedByThisField ? "Don't group by this field" : 'Group by this field' }}
</div>
</NcMenuItem>
</NcTooltip>
<a-divider class="!my-0" />
<NcMenuItem v-if="!column?.pk" :disabled="!isDuplicateAllowed" @click="openDuplicateDlg">
<div v-e="['a:field:duplicate']" class="nc-column-duplicate nc-header-menu-item">
@ -418,7 +499,10 @@ const isDuplicateAllowed = computed(() => {
}
}
:deep(.ant-dropdown-menu-item) {
:deep(.ant-dropdown-menu-item:not(.ant-dropdown-menu-item-disabled)) {
@apply !hover:text-black text-gray-700;
}
:deep(.ant-dropdown-menu-item.ant-dropdown-menu-item-disabled .nc-icon) {
@apply text-current;
}
</style>

62
packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue

@ -27,8 +27,8 @@ interface Props {
showLoading?: boolean
modelValue?: undefined | Filter[]
webHook?: boolean
draftFilter?: Partial<FilterType>
}
const props = withDefaults(defineProps<Props>(), {
nestedLevel: 0,
autoSave: true,
@ -38,7 +38,11 @@ const props = withDefaults(defineProps<Props>(), {
webHook: false,
})
const emit = defineEmits(['update:filtersLength'])
const emit = defineEmits(['update:filtersLength', 'update:draftFilter'])
const excludedFilterColUidt = [UITypes.QrCode, UITypes.Barcode]
const draftFilter = useVModel(props, 'draftFilter', emit)
const { nestedLevel, parentId, autoSave, hookId, modelValue, showLoading, webHook } = toRefs(props)
@ -94,8 +98,12 @@ const localNestedFilters = ref()
const wrapperDomRef = ref<HTMLElement>()
const addFiltersRowDomRef = ref<HTMLElement>()
const isMounted = ref(false)
const columns = computed(() => meta.value?.columns)
const fieldsToFilter = computed(() => (columns.value || []).filter((c) => !excludedFilterColUidt.includes(c.uidt as UITypes)))
const getColumn = (filter: Filter) => {
// extract looked up column if available
return btLookupTypesMap.value[filter.fk_column_id] || columns.value?.find((col: ColumnType) => col.id === filter.fk_column_id)
@ -279,8 +287,12 @@ const scrollDownIfNeeded = () => {
}
}
const addFilter = async () => {
await _addFilter()
const addFilter = async (filter?: Partial<FilterType>) => {
await _addFilter(false, filter)
if (filter) {
selectFilterField(filters.value[filters.value.length - 1], filters.value.length - 1)
}
if (!nested.value) {
// if nested, scroll to bottom
@ -316,12 +328,9 @@ const showFilterInput = (filter: Filter) => {
}
}
onMounted(() => {
loadFilters(hookId?.value, webHook.value)
})
onMounted(async () => {
await loadBtLookupTypes()
await Promise.all([loadFilters(hookId?.value, webHook.value), loadBtLookupTypes()])
isMounted.value = true
})
onBeforeUnmount(() => {
@ -331,6 +340,37 @@ onBeforeUnmount(() => {
function isDateType(uidt: UITypes) {
return [UITypes.Date, UITypes.DateTime, UITypes.CreatedTime, UITypes.LastModifiedTime].includes(uidt)
}
watch(
[draftFilter, isMounted],
async () => {
if (!isMounted.value || !draftFilter.value?.fk_column_id) return
await addFilter(draftFilter.value)
await nextTick()
scrollToBottom()
const filterWrapper = document.querySelectorAll(`.nc-filter-wrapper-${draftFilter.value.fk_column_id}`)
draftFilter.value = {}
if (!filterWrapper.length) return
const filterInputElement =
filterWrapper[filterWrapper.length - 1]?.querySelector<HTMLInputElement>('.nc-filter-value-select input')
if (filterInputElement) {
setTimeout(() => {
filterInputElement?.focus?.()
filterInputElement?.click?.()
}, 100)
}
},
{
deep: true,
immediate: true,
},
)
</script>
<template>
@ -405,7 +445,7 @@ function isDateType(uidt: UITypes) {
</div>
</div>
</template>
<div v-else class="flex flex-row gap-x-2 w-full">
<div v-else class="flex flex-row gap-x-2 w-full" :class="`nc-filter-wrapper-${filter.fk_column_id}`">
<span v-if="!i" class="flex items-center ml-2 mr-7.35">{{ $t('labels.where') }}</span>
<NcSelect
@ -436,7 +476,7 @@ function isDateType(uidt: UITypes) {
:key="`${i}_6`"
v-model="filter.fk_column_id"
class="nc-filter-field-select min-w-32 max-w-32 max-h-8"
:columns="columns"
:columns="fieldsToFilter"
:disabled="filter.readOnly"
@click.stop
@change="selectFilterField(filter, i)"

18
packages/nc-gui/components/smartsheet/toolbar/ColumnFilterMenu.vue

@ -1,8 +1,10 @@
<script setup lang="ts">
import type { ColumnType } from 'nocodb-sdk'
import {
ActiveViewInj,
AllFiltersInj,
IsLockedInj,
SmartsheetStoreEvents,
computed,
iconMap,
inject,
@ -22,7 +24,7 @@ const { isMobileMode } = useGlobal()
const filterComp = ref<typeof ColumnFilter>()
const { nestedFilters } = useSmartsheetStoreOrThrow()
const { nestedFilters, eventBus } = useSmartsheetStoreOrThrow()
// todo: avoid duplicate api call by keeping a filter store
const { nonDeletedFilters, loadFilters } = useViewFilters(
@ -40,7 +42,7 @@ watch(
() => activeView?.value?.id,
async (viewId) => {
if (viewId) {
await loadFilters()
await loadFilters(undefined, false, true)
filtersLength.value = nonDeletedFilters.value.length || 0
}
},
@ -54,6 +56,17 @@ const allFilters = ref({})
provide(AllFiltersInj, allFilters)
useMenuCloseOnEsc(open)
const draftFilter = ref({})
eventBus.on(async (event, column: ColumnType) => {
if (!column) return
if (event === SmartsheetStoreEvents.FILTER_ADD) {
draftFilter.value = { fk_column_id: column.id }
open.value = true
}
})
</script>
<template>
@ -78,6 +91,7 @@ useMenuCloseOnEsc(open)
<template #overlay>
<SmartsheetToolbarColumnFilter
ref="filterComp"
v-model:draft-filter="draftFilter"
class="nc-table-toolbar-menu"
:auto-save="true"
data-testid="nc-filter-menu"

75
packages/nc-gui/components/smartsheet/toolbar/GroupByMenu.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import type { ColumnType, LinkToAnotherRecordType, LookupType } from 'nocodb-sdk'
import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk'
import { RelationTypes, UITypes, isLinksOrLTAR, isSystemColumn } from 'nocodb-sdk'
import {
ActiveViewInj,
@ -8,30 +8,26 @@ import {
computed,
getSortDirectionOptions,
inject,
onMounted,
ref,
useMenuCloseOnEsc,
useMetas,
useNuxtApp,
useSmartsheetStoreOrThrow,
useViewColumnsOrThrow,
watch,
} from '#imports'
const excludedGroupingUidt = [UITypes.Attachment]
const meta = inject(MetaInj, ref())
const view = inject(ActiveViewInj, ref())
const isLocked = inject(IsLockedInj, ref(false))
const { gridViewCols, updateGridViewColumn, metaColumnById, showSystemFields } = useViewColumnsOrThrow()
const { fieldsToGroupBy, groupByLimit } = useViewGroupBy(view)
const { $e } = useNuxtApp()
const _groupBy = ref<{ fk_column_id?: string; sort: string; order: number }[]>([])
const { getMeta } = useMetas()
const groupBy = computed<{ fk_column_id?: string; sort: string; order: number }[]>(() => {
const tempGroupBy: { fk_column_id?: string; sort: string; order: number }[] = []
Object.values(gridViewCols.value).forEach((col) => {
@ -53,24 +49,8 @@ const { eventBus } = useSmartsheetStoreOrThrow()
const { isMobileMode } = useGlobal()
const supportedLookups = ref<string[]>([])
const showCreateGroupBy = ref(false)
const fieldsToGroupBy = computed(() => {
const fields = meta.value?.columns || []
return fields.filter((field) => {
if (excludedGroupingUidt.includes(field.uidt as UITypes)) return false
if (field.uidt === UITypes.Lookup) {
return field.id && supportedLookups.value.includes(field.id)
}
return true
})
})
const columns = computed(() => meta.value?.columns || [])
const columnByID = computed(() =>
@ -174,49 +154,18 @@ watch(open, () => {
}
})
const loadAllowedLookups = async () => {
const filteredLookupCols = []
try {
for (const col of meta.value?.columns || []) {
if (col.uidt !== UITypes.Lookup) continue
let nextCol: ColumnType = col
// check the lookup column is supported type or not
while (nextCol && nextCol.uidt === UITypes.Lookup) {
const lookupRelation = (await getMeta(nextCol.fk_model_id as string))?.columns?.find(
(c) => c.id === (nextCol?.colOptions as LookupType).fk_relation_column_id,
)
const relatedTableMeta = await getMeta(
(lookupRelation?.colOptions as LinkToAnotherRecordType).fk_related_model_id as string,
)
eventBus.on(async (event, column) => {
if (!column?.id) return
nextCol = relatedTableMeta?.columns?.find(
(c) => c.id === ((nextCol?.colOptions as LookupType).fk_lookup_column_id as string),
) as ColumnType
if (event === SmartsheetStoreEvents.GROUP_BY_ADD) {
addFieldToGroupBy(column)
} else if (event === SmartsheetStoreEvents.GROUP_BY_REMOVE) {
if (groupedByColumnIds.value.length === 0) return
// if next column is same as root lookup column then break the loop
// since it's going to be a circular loop, and ignore the column
if (nextCol?.id === col.id) {
break
}
}
if (nextCol?.uidt !== UITypes.Attachment && col.id) filteredLookupCols.push(col.id)
}
_groupBy.value = _groupBy.value.filter((g) => g.fk_column_id !== column.id)
supportedLookups.value = filteredLookupCols
} catch (e) {
console.error(e)
await saveGroupBy()
}
}
onMounted(async () => {
await loadAllowedLookups()
})
watch(meta, async () => {
await loadAllowedLookups()
})
</script>
@ -305,7 +254,7 @@ watch(meta, async () => {
</template>
</div>
<NcDropdown
v-if="availableColumns.length && fieldsToGroupBy.length > _groupBy.length && _groupBy.length < 3"
v-if="availableColumns.length && fieldsToGroupBy.length > _groupBy.length && _groupBy.length < groupByLimit"
v-model:visible="showCreateGroupBy"
:trigger="['click']"
overlay-class-name="nc-toolbar-dropdown"

3
packages/nc-gui/composables/useSmartsheetStore.ts

@ -58,6 +58,8 @@ const [useProvideSmartsheetStore, useSmartsheetStore] = useInjectionState(
const sorts = ref<SortType[]>(unref(initialSorts) ?? [])
const nestedFilters = ref<FilterType[]>(unref(initialFilters) ?? [])
const allFilters = ref<Filter[]>([])
watch(
sorts,
() => {
@ -97,6 +99,7 @@ const [useProvideSmartsheetStore, useSmartsheetStore] = useInjectionState(
isSqlView,
eventBus,
sqlUi,
allFilters,
}
},
'smartsheet-store',

71
packages/nc-gui/composables/useViewFilters.ts

@ -43,7 +43,7 @@ export function useViewFilters(
const reloadHook = inject(ReloadViewDataHookInj)
const { nestedFilters } = useSmartsheetStoreOrThrow()
const { nestedFilters, allFilters } = useSmartsheetStoreOrThrow()
const { baseMeta } = storeToRefs(useBase())
@ -208,7 +208,35 @@ export function useViewFilters(
}
}
const loadFilters = async (hookId?: string, isWebhook = false) => {
const loadAllChildFilters = async (filters: Filter[]) => {
// Array to store promises of child filter loading
const promises = []
// Array to store all child filters
const allChildFilters: Filter[] = []
// Iterate over all filters
for (const filter of filters) {
// Check if the filter is a group
if (filter.id && filter.is_group) {
// Load children filters from the backend
const childFilterPromise = $api.dbTableFilter.childrenRead(filter.id).then((response) => {
const childFilters = response.list as Filter[]
allChildFilters.push(...childFilters)
return loadAllChildFilters(childFilters)
})
promises.push(childFilterPromise)
}
}
// Wait for all promises to resolve
await Promise.all(promises)
// Push all child filters into the allFilters array
allFilters.value.push(...allChildFilters)
}
const loadFilters = async (hookId?: string, isWebhook = false, loadAllFilters = false) => {
if (!view.value?.id) return
if (nestedMode.value) {
@ -228,6 +256,10 @@ export function useViewFilters(
filters.value = (await $api.dbTableFilter.childrenRead(parentId)).list as Filter[]
} else {
filters.value = (await $api.dbTableFilter.read(view.value!.id!)).list as Filter[]
if (loadAllFilters) {
allFilters.value = [...filters.value]
await loadAllChildFilters(allFilters.value)
}
}
}
} catch (e: any) {
@ -241,6 +273,11 @@ export function useViewFilters(
for (const [i, filter] of Object.entries(filters.value)) {
if (filter.status === 'delete') {
await $api.dbTableFilter.delete(filter.id as string)
if (filter.is_group) {
deleteFilterGroupFromAllFilters(filter)
} else {
allFilters.value = allFilters.value.filter((f) => f.id !== filter.id)
}
} else if (filter.status === 'update') {
await $api.dbTableFilter.update(filter.id as string, {
...filter,
@ -258,6 +295,8 @@ export function useViewFilters(
fk_parent_id: parentId,
})
}
allFilters.value.push(filters.value[+i])
}
}
@ -323,6 +362,8 @@ export function useViewFilters(
...filter,
fk_parent_id: parentId,
})
allFilters.value.push(filters.value[+i])
}
} catch (e: any) {
console.log(e)
@ -334,6 +375,21 @@ export function useViewFilters(
if (!isWebhook) reloadData?.()
}
function deleteFilterGroupFromAllFilters(filter: Filter) {
// Find all child filters of the specified parentId
const childFilters = allFilters.value.filter((f) => f.fk_parent_id === filter.id)
// Recursively delete child filter of child filter
childFilters.forEach((childFilter) => {
if (childFilter.is_group) {
deleteFilterGroupFromAllFilters(childFilter)
}
})
// Remove the parent object and its children from the array
allFilters.value = allFilters.value.filter((f) => f.id !== filter.id && f.fk_parent_id !== filter.id)
}
const deleteFilter = async (filter: Filter, i: number, undo = false) => {
if (!undo && !filter.is_group) {
addUndo({
@ -369,6 +425,7 @@ export function useViewFilters(
} else {
try {
await $api.dbTableFilter.delete(filter.id)
if (!isWebhook) reloadData?.()
filters.value.splice(i, 1)
} catch (e: any) {
@ -382,12 +439,18 @@ export function useViewFilters(
}
$e('a:filter:delete', { length: nonDeletedFilters.value.length })
}
if (filter.is_group) {
deleteFilterGroupFromAllFilters(filter)
} else {
allFilters.value = allFilters.value.filter((f) => f.id !== filter.id)
}
}
const saveOrUpdateDebounced = useDebounceFn(saveOrUpdate, 500)
const addFilter = async (undo = false) => {
filters.value.push(placeholderFilter())
const addFilter = async (undo = false, draftFilter: Partial<FilterType> = {}) => {
filters.value.push(draftFilter?.fk_column_id ? { ...placeholderFilter(), ...draftFilter } : placeholderFilter())
if (!undo) {
addUndo({
undo: {

83
packages/nc-gui/composables/useViewGroupBy.ts

@ -1,11 +1,16 @@
import { type ColumnType, type SelectOptionsType, UITypes, type ViewType } from 'nocodb-sdk'
import { UITypes } from 'nocodb-sdk'
import type { ColumnType, LinkToAnotherRecordType, LookupType, SelectOptionsType, ViewType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { message } from 'ant-design-vue'
import { extractSdkResponseErrorMsg } from '../utils'
import { GROUP_BY_VARS, ref, storeToRefs, useApi, useBase, useViewColumnsOrThrow } from '#imports'
import { GROUP_BY_VARS, ref, storeToRefs, useApi, useBase, useMetas, useViewColumnsOrThrow } from '#imports'
import type { Group, GroupNestedIn, Row } from '#imports'
const excludedGroupingUidt = [UITypes.Attachment, UITypes.QrCode, UITypes.Barcode]
export const useViewGroupBy = (view: Ref<ViewType | undefined>, where?: ComputedRef<string | undefined>) => {
const groupByLimit: number = 3
const { api } = useApi()
const { appInfo } = useGlobal()
@ -18,6 +23,8 @@ export const useViewGroupBy = (view: Ref<ViewType | undefined>, where?: Computed
const { gridViewCols } = useViewColumnsOrThrow()
const { getMeta } = useMetas()
const groupBy = computed<{ column: ColumnType; sort: string; order?: number }[]>(() => {
const tempGroupBy: { column: ColumnType; sort: string; order?: number }[] = []
Object.values(gridViewCols.value).forEach((col) => {
@ -54,6 +61,20 @@ export const useViewGroupBy = (view: Ref<ViewType | undefined>, where?: Computed
return appInfo.value.defaultGroupByLimit?.limitRecord || 10
})
const supportedLookups = ref<string[]>([])
const fieldsToGroupBy = computed(() =>
(meta?.value?.columns || []).filter((field) => {
if (excludedGroupingUidt.includes(field.uidt as UITypes)) return false
if (field.uidt === UITypes.Lookup) {
return field.id && supportedLookups.value.includes(field.id)
}
return true
}),
)
const rootGroup = ref<Group>({
key: 'root',
color: 'root',
@ -464,5 +485,61 @@ export const useViewGroupBy = (view: Ref<ViewType | undefined>, where?: Computed
}
}
return { rootGroup, groupBy, isGroupBy, loadGroups, loadGroupData, loadGroupPage, groupWrapperChangePage, redistributeRows }
const loadAllowedLookups = async () => {
const filteredLookupCols = []
try {
for (const col of meta?.value?.columns || []) {
if (col.uidt !== UITypes.Lookup) continue
let nextCol: ColumnType = col
// check the lookup column is supported type or not
while (nextCol && nextCol.uidt === UITypes.Lookup) {
const lookupRelation = (await getMeta(nextCol.fk_model_id as string))?.columns?.find(
(c) => c.id === (nextCol?.colOptions as LookupType).fk_relation_column_id,
)
const relatedTableMeta = await getMeta(
(lookupRelation?.colOptions as LinkToAnotherRecordType).fk_related_model_id as string,
)
nextCol = relatedTableMeta?.columns?.find(
(c) => c.id === ((nextCol?.colOptions as LookupType).fk_lookup_column_id as string),
) as ColumnType
// if next column is same as root lookup column then break the loop
// since it's going to be a circular loop, and ignore the column
if (nextCol?.id === col.id) {
break
}
}
if (nextCol?.uidt !== UITypes.Attachment && col.id) filteredLookupCols.push(col.id)
}
supportedLookups.value = filteredLookupCols
} catch (e) {
console.error(e)
}
}
onMounted(async () => {
await loadAllowedLookups()
})
watch(meta, async () => {
await loadAllowedLookups()
})
return {
rootGroup,
groupBy,
isGroupBy,
fieldsToGroupBy,
groupByLimit,
loadGroups,
loadGroupData,
loadGroupPage,
groupWrapperChangePage,
redistributeRows,
}
}

3
packages/nc-gui/lib/enums.ts

@ -81,6 +81,9 @@ export enum SmartsheetStoreEvents {
FIELD_ADD = 'field-add',
MAPPED_BY_COLUMN_CHANGE = 'mapped-by-column-change',
CLEAR_NEW_ROW = 'clear-new-row',
GROUP_BY_ADD = 'group-by-add',
GROUP_BY_REMOVE = 'group-by-remove',
FILTER_ADD = 'filter-add',
}
export enum DataSourcesSubTab {

Loading…
Cancel
Save