mirror of
https://github.com/nocodb/nocodb.git
synced 2026-05-01 04:06:48 +00:00
769 lines
25 KiB
TypeScript
769 lines
25 KiB
TypeScript
import type {
|
|
ButtonType,
|
|
ColumnType,
|
|
GridColumnReqType,
|
|
GridColumnType,
|
|
ListType,
|
|
MapType,
|
|
TableType,
|
|
ViewType,
|
|
} from 'nocodb-sdk'
|
|
import { CommonAggregations, ViewTypes, getFirstNonPersonalView, isHiddenCol, isSystemColumn } from 'nocodb-sdk'
|
|
import type { ComputedRef, Ref } from 'vue'
|
|
|
|
const [useProvideViewColumns, useViewColumns] = useInjectionState(
|
|
(
|
|
view: Ref<ViewType | undefined>,
|
|
meta: Ref<TableType | undefined> | ComputedRef<TableType | undefined>,
|
|
reloadData?: (params?: { shouldShowLoading?: boolean }) => void,
|
|
isPublic = false,
|
|
) => {
|
|
const rootFields = ref<ColumnType[]>([])
|
|
|
|
const fields = ref<Field[]>()
|
|
|
|
const fieldsMap = computed(() =>
|
|
(fields.value || []).reduce<Record<string, any>>((acc, curr) => {
|
|
if (curr.fk_column_id) {
|
|
acc[curr.fk_column_id] = curr
|
|
}
|
|
return acc
|
|
}, {}),
|
|
)
|
|
|
|
const filterQuery = ref('')
|
|
|
|
const { $api, $e, $eventBus } = useNuxtApp()
|
|
|
|
const { getMeta: _getMeta, getMetaByKey: _getMetaByKey } = useMetas()
|
|
|
|
const { t } = useI18n()
|
|
|
|
const { isUIAllowed } = useRoles()
|
|
|
|
const { isSharedBase } = storeToRefs(useBase())
|
|
|
|
const viewStore = useViewsStore()
|
|
|
|
const { views } = storeToRefs(viewStore)
|
|
|
|
const isDefaultView = computed(() => {
|
|
return (
|
|
getFirstNonPersonalView(views.value, {
|
|
includeViewType: ViewTypes.GRID,
|
|
})?.id === view.value?.id
|
|
)
|
|
})
|
|
|
|
const isViewColumnsLoading = ref(true)
|
|
|
|
const hidingViewColumnsMap = ref<Record<string, boolean>>({})
|
|
|
|
const { addUndo, defineViewScope } = useUndoRedo()
|
|
|
|
const { hasPersonalViewPermission } = usePersonalViewPermissions(view)
|
|
|
|
const canEditViewFields = hasPersonalViewPermission('viewFieldEdit')
|
|
|
|
const isLocalMode = computed(() => isPublic || !canEditViewFields.value || isSharedBase.value)
|
|
|
|
const hasViewFieldDataEditPermission = computed(() => isUIAllowed('viewFieldDataEdit'))
|
|
|
|
const canUpdateViewMeta = hasPersonalViewPermission('viewCreateOrEdit')
|
|
|
|
const localChanges = ref<Record<string, Field>>({})
|
|
|
|
const isColumnViewEssential = (column: ColumnType) => {
|
|
// TODO: consider at some point ti delegate this via a cleaner design pattern to view specific check logic
|
|
// which could be inside of a view specific helper class (and generalized via an interface)
|
|
// (on the other hand, the logic complexity is still very low atm - might be overkill)
|
|
return view.value?.type === ViewTypes.MAP && (view.value?.view as MapType)?.fk_geo_data_col_id === column.id
|
|
}
|
|
|
|
const metaColumnById = computed<Record<string, ColumnType>>(() => {
|
|
const result: Record<string, ColumnType> = {}
|
|
|
|
for (const col of (meta.value?.columns || []) as ColumnType[]) {
|
|
if (col.id) result[col.id] = col
|
|
}
|
|
|
|
// Include level table columns for list views (from shared metas cache)
|
|
if (view.value?.type === ViewTypes.LIST) {
|
|
const levels = (view.value?.view as ListType)?.levels || []
|
|
for (const level of levels) {
|
|
if (level.fk_model_id && level.fk_model_id !== meta.value?.id) {
|
|
const tableMeta = _getMetaByKey(meta.value?.base_id, level.fk_model_id)
|
|
if (tableMeta?.columns) {
|
|
for (const col of tableMeta.columns as ColumnType[]) {
|
|
if (col.id) result[col.id] = col
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
})
|
|
|
|
const gridViewCols = ref<Record<string, GridColumnType>>({})
|
|
|
|
const loadViewColumns = async () => {
|
|
if (!meta.value || !view.value?.id) return
|
|
|
|
let order = 1
|
|
|
|
const data =
|
|
((isPublic
|
|
? meta.value?.columns
|
|
: (
|
|
await $api.internal.getOperation(meta.value!.fk_workspace_id!, meta.value!.base_id!, {
|
|
operation: 'viewColumnList',
|
|
viewId: view.value.id,
|
|
})
|
|
).list) as any[]) ?? []
|
|
|
|
const fieldById = data.reduce<Record<string, any>>((acc, curr) => {
|
|
// If hide column api is in progress and we try to load columns before that then we need to assign local visibility state
|
|
curr.show = hidingViewColumnsMap.value[curr.fk_column_id] && !!curr.show ? false : !!curr.show
|
|
|
|
return {
|
|
...acc,
|
|
[curr.fk_column_id]: curr,
|
|
}
|
|
}, {})
|
|
|
|
// For list views with levels, ensure metas for non-root level tables are loaded
|
|
if (view.value?.type === ViewTypes.LIST) {
|
|
const levels = (view.value?.view as ListType)?.levels || []
|
|
|
|
for (const level of levels) {
|
|
if (level.fk_model_id && level.fk_model_id !== meta.value?.id) {
|
|
try {
|
|
await _getMeta(meta.value!.base_id!, level.fk_model_id)
|
|
} catch (e) {
|
|
// silently ignore — level table meta may not be accessible
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build combined columns: root table + level tables (for list views)
|
|
const allTableColumns: { column: ColumnType; tableMeta: TableType }[] = (meta.value?.columns || []).map(
|
|
(col: ColumnType) => ({
|
|
column: col,
|
|
tableMeta: meta.value!,
|
|
}),
|
|
)
|
|
|
|
if (view.value?.type === ViewTypes.LIST) {
|
|
const levels = (view.value?.view as ListType)?.levels || []
|
|
// Track existing column IDs to avoid duplicates
|
|
// (public views already include level columns in meta.value?.columns)
|
|
const existingColIds = new Set(allTableColumns.map(({ column }) => column.id))
|
|
for (const level of levels) {
|
|
if (level.fk_model_id && level.fk_model_id !== meta.value?.id) {
|
|
const tableMeta = _getMetaByKey(meta.value?.base_id, level.fk_model_id)
|
|
if (tableMeta?.columns) {
|
|
for (const col of tableMeta.columns as ColumnType[]) {
|
|
if (!existingColIds.has(col.id)) {
|
|
allTableColumns.push({ column: col, tableMeta })
|
|
existingColIds.add(col.id)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fields.value = allTableColumns
|
|
.filter(({ column, tableMeta }) => {
|
|
// filter created by and last modified by system columns
|
|
if (isHiddenCol(column, tableMeta)) return false
|
|
return true
|
|
})
|
|
.map(({ column }) => {
|
|
const currentColumnField = fieldById[column.id!] || {}
|
|
|
|
return {
|
|
title: column.title,
|
|
fk_column_id: column.id,
|
|
...currentColumnField,
|
|
show: currentColumnField.show || isColumnViewEssential(currentColumnField),
|
|
order: currentColumnField.order || order++,
|
|
aggregation: currentColumnField?.aggregation ?? CommonAggregations.None,
|
|
system: isSystemColumn(metaColumnById?.value?.[currentColumnField.fk_column_id!]),
|
|
isViewEssentialField: isColumnViewEssential(column),
|
|
initialShow:
|
|
currentColumnField.show ||
|
|
isColumnViewEssential(currentColumnField) ||
|
|
(currentColumnField as GridColumnType)?.group_by,
|
|
}
|
|
})
|
|
.sort((a: Field, b: Field) => a.order - b.order)
|
|
|
|
if (isLocalMode.value && fields.value) {
|
|
for (const key in localChanges.value) {
|
|
const fieldIndex = fields.value.findIndex((f) => f.fk_column_id === key)
|
|
if (fieldIndex !== undefined && fieldIndex > -1) {
|
|
fields.value[fieldIndex] = localChanges.value[key]
|
|
fields.value = fields.value.sort((a: Field, b: Field) => a.order - b.order)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use fields columns to populate gridViewCols
|
|
gridViewCols.value = fields.value.reduce<Record<string, GridColumnType>>(
|
|
(o, col) => ({
|
|
...o,
|
|
[col.fk_column_id as string]: col,
|
|
}),
|
|
{},
|
|
)
|
|
}
|
|
|
|
const updateDefaultViewColumnMeta = async (
|
|
columnId?: string,
|
|
colMeta: { defaultViewColOrder?: number; defaultViewColVisibility?: boolean } = {},
|
|
allFields = false,
|
|
) => {
|
|
if (!meta.value?.columns) return
|
|
|
|
meta.value.columns = (meta.value.columns || []).map((c: ColumnType) => {
|
|
if (!allFields && c.id !== columnId) return c
|
|
|
|
if (allFields && c.pv) return c
|
|
|
|
c.meta = { ...parseProp(c.meta || {}), ...colMeta }
|
|
return c
|
|
})
|
|
|
|
if (!allFields && columnId && meta.value?.columnsById?.[columnId]) {
|
|
meta.value.columnsById[columnId].meta = {
|
|
...parseProp(meta.value.columnsById[columnId].meta),
|
|
...colMeta,
|
|
}
|
|
}
|
|
|
|
if (allFields) {
|
|
meta.value.columnsById = meta.value.columns.reduce((acc, c) => {
|
|
acc[c.id!] = c
|
|
|
|
return acc
|
|
}, {} as Record<string, ColumnType>)
|
|
}
|
|
}
|
|
|
|
const showAll = async (ignoreIds?: any, levelId?: string) => {
|
|
if (isLocalMode.value) {
|
|
const fieldById = (fields.value || []).reduce<Record<string, any>>((acc, curr) => {
|
|
if (curr.fk_column_id) {
|
|
curr.show = !!curr.initialShow
|
|
acc[curr.fk_column_id] = curr
|
|
}
|
|
return acc
|
|
}, {})
|
|
|
|
fields.value = (fields.value || [])?.map((field: Field) => {
|
|
const updateField = {
|
|
...field,
|
|
show: fieldById[field.fk_column_id!]?.show,
|
|
}
|
|
localChanges.value[field.fk_column_id!] = field
|
|
return updateField
|
|
})
|
|
|
|
meta.value!.columns = meta.value!.columns?.map((column: ColumnType) => {
|
|
if (fieldById[column.id!]) {
|
|
return {
|
|
...column,
|
|
...fieldById[column.id!],
|
|
id: fieldById[column.id!].fk_column_id,
|
|
}
|
|
}
|
|
return column
|
|
})
|
|
|
|
reloadData?.()
|
|
return
|
|
}
|
|
|
|
if (view?.value?.id) {
|
|
await $api.internal.postOperation(view.value.fk_workspace_id!, view.value.base_id!, {
|
|
operation: 'showAllColumns',
|
|
viewId: view.value.id,
|
|
...(ignoreIds ? { ignoreIds } : {}),
|
|
...(levelId ? { levelId } : {}),
|
|
})
|
|
|
|
if (isDefaultView.value) {
|
|
updateDefaultViewColumnMeta(undefined, { defaultViewColVisibility: true }, true)
|
|
}
|
|
}
|
|
|
|
await loadViewColumns()
|
|
reloadData?.()
|
|
$e('a:fields:show-all')
|
|
}
|
|
|
|
const hideAll = async (ignoreIds?: any, levelId?: string) => {
|
|
if (isLocalMode.value) {
|
|
const fieldById = (fields.value || []).reduce<Record<string, any>>((acc, curr) => {
|
|
if (curr.fk_column_id) {
|
|
curr.show = !!metaColumnById?.value?.[curr.fk_column_id!]?.pv || !!curr.isViewEssentialField
|
|
acc[curr.fk_column_id] = curr
|
|
}
|
|
return acc
|
|
}, {})
|
|
|
|
fields.value = (fields.value || [])?.map((field: Field) => {
|
|
const updateField = {
|
|
...field,
|
|
show: fieldById[field.fk_column_id!]?.show,
|
|
}
|
|
localChanges.value[field.fk_column_id!] = field
|
|
return updateField
|
|
})
|
|
|
|
meta.value!.columns = meta.value!.columns?.map((column: ColumnType) => {
|
|
if (fieldById[column.id!]) {
|
|
return {
|
|
...column,
|
|
...fieldById[column.id!],
|
|
id: fieldById[column.id!].fk_column_id,
|
|
}
|
|
}
|
|
return column
|
|
})
|
|
|
|
reloadData?.()
|
|
return
|
|
}
|
|
if (view?.value?.id) {
|
|
await $api.internal.postOperation(view.value.fk_workspace_id!, view.value.base_id!, {
|
|
operation: 'hideAllColumns',
|
|
viewId: view.value.id,
|
|
...(ignoreIds ? { ignoreIds } : {}),
|
|
...(levelId ? { levelId } : {}),
|
|
})
|
|
|
|
if (isDefaultView.value) {
|
|
updateDefaultViewColumnMeta(undefined, { defaultViewColVisibility: false }, true)
|
|
}
|
|
}
|
|
|
|
await loadViewColumns()
|
|
reloadData?.()
|
|
$e('a:fields:show-all')
|
|
}
|
|
|
|
const saveOrUpdate = async (field: any, index: number, disableDataReload = false, updateDefaultViewColMeta = false) => {
|
|
if (isLocalMode.value && fields.value) {
|
|
fields.value[index] = field
|
|
meta.value!.columns = meta.value!.columns?.map((column: ColumnType) => {
|
|
if (column.id === field.fk_column_id) {
|
|
return {
|
|
...column,
|
|
...field,
|
|
id: field.fk_column_id,
|
|
}
|
|
}
|
|
return column
|
|
})
|
|
|
|
localChanges.value[field.fk_column_id] = field
|
|
}
|
|
|
|
if (canEditViewFields.value) {
|
|
if (field.id && view?.value?.id) {
|
|
await $api.internal.postOperation(
|
|
meta.value!.fk_workspace_id!,
|
|
meta.value!.base_id!,
|
|
{
|
|
operation: 'viewColumnUpdate',
|
|
viewId: view.value.id,
|
|
columnId: field.id,
|
|
},
|
|
field,
|
|
)
|
|
|
|
if (updateDefaultViewColMeta) {
|
|
updateDefaultViewColumnMeta(field.fk_column_id, {
|
|
defaultViewColOrder: field.order,
|
|
defaultViewColVisibility: field.show,
|
|
})
|
|
}
|
|
} else if (view.value?.id) {
|
|
const insertedField = (await $api.internal.postOperation(
|
|
meta.value!.fk_workspace_id!,
|
|
meta.value!.base_id!,
|
|
{
|
|
operation: 'viewColumnCreate',
|
|
viewId: view.value.id,
|
|
},
|
|
field,
|
|
)) as any
|
|
|
|
/** update the field in fields if defined */
|
|
if (fields.value) fields.value[index] = insertedField
|
|
|
|
return insertedField
|
|
}
|
|
}
|
|
|
|
if (!disableDataReload) {
|
|
await loadViewColumns()
|
|
reloadData?.({ shouldShowLoading: false })
|
|
}
|
|
}
|
|
|
|
const showSystemFields = computed({
|
|
get() {
|
|
return (view.value?.show_system_fields as boolean) || false
|
|
},
|
|
set(v: boolean) {
|
|
if (view?.value?.id) {
|
|
if (!isLocalMode.value) {
|
|
$api.internal
|
|
.postOperation(
|
|
view.value.fk_workspace_id!,
|
|
view.value.base_id!,
|
|
{
|
|
operation: 'viewUpdate',
|
|
viewId: view.value.id,
|
|
},
|
|
{
|
|
show_system_fields: v,
|
|
},
|
|
)
|
|
.finally(() => {
|
|
loadViewColumns()
|
|
reloadData?.()
|
|
})
|
|
}
|
|
view.value.show_system_fields = v
|
|
}
|
|
$e('a:fields:system-fields')
|
|
},
|
|
})
|
|
|
|
const fieldSearchBasisOptions: {
|
|
searchBasisInfo: string
|
|
filterCallback: (query: string, option: ColumnType) => boolean
|
|
}[] = [
|
|
{
|
|
searchBasisInfo: t('msg.info.matchedByButtonLabel'),
|
|
filterCallback: (query, option) => {
|
|
if (!option) return false
|
|
|
|
const column = option as ColumnType
|
|
|
|
return isButton(column) && searchCompare([(column.colOptions as ButtonType)?.label], query)
|
|
},
|
|
},
|
|
{
|
|
searchBasisInfo: t('msg.info.matchedByFieldDescription'),
|
|
filterCallback: (query, option) => {
|
|
if (!option) return false
|
|
|
|
const column = option as ColumnType
|
|
|
|
if (!column.description) return false
|
|
|
|
return searchCompare([column.description], query)
|
|
},
|
|
},
|
|
]
|
|
|
|
const searchBasisIdMap = ref<Record<string, string>>({})
|
|
|
|
/**
|
|
* Apply search basis filter to the column
|
|
* @param column - The column to apply the search basis filter to
|
|
* @returns true if the column matches the search basis filter, false otherwise
|
|
*/
|
|
const applySearchBasisFilter = (column?: ColumnType) => {
|
|
if (!column) return false
|
|
|
|
for (const basisOption of fieldSearchBasisOptions) {
|
|
if (!basisOption.filterCallback(filterQuery.value, column)) continue
|
|
|
|
searchBasisIdMap.value[column.id!] = basisOption.searchBasisInfo
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
const filteredFieldList = computed(() => {
|
|
searchBasisIdMap.value = {}
|
|
|
|
return (fields.value || []).filter((field: Field) => {
|
|
if (!field.initialShow && isLocalMode.value && !hasViewFieldDataEditPermission.value) {
|
|
return false
|
|
}
|
|
const column = metaColumnById?.value?.[field.fk_column_id!]
|
|
|
|
if (column?.pv) {
|
|
// Step 1: Apply default filter
|
|
if (!filterQuery.value || searchCompare([field.title], filterQuery.value)) return true
|
|
|
|
// Step 2: Apply search basis options if default filter fails
|
|
return applySearchBasisFilter(column)
|
|
}
|
|
|
|
// hide system columns if not enabled
|
|
if (!showSystemFields.value && isSystemColumn(column)) {
|
|
return false
|
|
}
|
|
|
|
// Step 1: Apply default filter
|
|
if (!filterQuery.value || searchCompare([field.title], filterQuery.value)) return true
|
|
|
|
// Step 2: Apply search basis options if default filter fails
|
|
return applySearchBasisFilter(column)
|
|
})
|
|
})
|
|
|
|
const numberOfHiddenFields = computed(() => {
|
|
return (fields.value || [])
|
|
?.filter((field: Field) => {
|
|
if (!field.initialShow && isLocalMode.value && !hasViewFieldDataEditPermission.value) {
|
|
return false
|
|
}
|
|
|
|
if (metaColumnById?.value?.[field.fk_column_id!]?.pv) {
|
|
return true
|
|
}
|
|
|
|
// hide system columns if not enabled
|
|
if (!showSystemFields.value && isSystemColumn(metaColumnById?.value?.[field.fk_column_id!])) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
})
|
|
.filter((field) => !field.show)?.length
|
|
})
|
|
|
|
const sortedAndFilteredFields = computed<ColumnType[]>(() => {
|
|
return (fields?.value
|
|
?.filter((field: Field) => {
|
|
// hide system columns if not enabled
|
|
if (
|
|
!showSystemFields.value &&
|
|
metaColumnById.value &&
|
|
metaColumnById?.value?.[field.fk_column_id!] &&
|
|
isSystemColumn(metaColumnById.value?.[field.fk_column_id!]) &&
|
|
!metaColumnById.value?.[field.fk_column_id!]?.pv
|
|
) {
|
|
return false
|
|
}
|
|
return field.show && metaColumnById?.value?.[field.fk_column_id!]
|
|
})
|
|
?.sort((a: Field, b: Field) => a.order - b.order)
|
|
?.map((field: Field) => metaColumnById?.value?.[field.fk_column_id!]) || []) as ColumnType[]
|
|
})
|
|
|
|
const toggleFieldVisibility = (checked: boolean, field: any) => {
|
|
const fieldIndex = fields.value?.findIndex((f) => f.fk_column_id === field.fk_column_id)
|
|
|
|
if (!fieldIndex && fieldIndex !== 0) return
|
|
addUndo({
|
|
undo: {
|
|
fn: (v: boolean) => {
|
|
field.show = !v
|
|
saveOrUpdate(field, fieldIndex, false, isDefaultView.value)
|
|
},
|
|
args: [checked],
|
|
},
|
|
redo: {
|
|
fn: (v: boolean) => {
|
|
field.show = v
|
|
saveOrUpdate(field, fieldIndex, false, isDefaultView.value)
|
|
},
|
|
args: [checked],
|
|
},
|
|
scope: defineViewScope({ view: view.value }),
|
|
})
|
|
saveOrUpdate(field, fieldIndex, !checked, isDefaultView.value)
|
|
}
|
|
|
|
const toggleFieldStyles = (field: any, style: 'underline' | 'bold' | 'italic', status: boolean) => {
|
|
const fieldIndex = fields.value?.findIndex((f) => f.fk_column_id === field.fk_column_id)
|
|
if (!fieldIndex && fieldIndex !== 0) return
|
|
field[style] = status
|
|
$e('a:fields:style', { style, status })
|
|
saveOrUpdate(field, fieldIndex, true)
|
|
}
|
|
|
|
// reload view columns when active view changes
|
|
// or when columns changes(delete/add)
|
|
watch(
|
|
[() => view?.value?.id, () => meta.value?.columns],
|
|
async ([newViewId], [oldViewId]) => {
|
|
// If we change view, we need to reset the hidingViewColumnsMap
|
|
if (oldViewId && oldViewId !== newViewId) {
|
|
hidingViewColumnsMap.value = {}
|
|
}
|
|
|
|
if (!ncIsEmptyArray(hidingViewColumnsMap.value) && Object.values(hidingViewColumnsMap.value).some((v) => v)) return
|
|
|
|
// reload only if view belongs to current table
|
|
if (newViewId && view.value?.fk_model_id === meta.value?.id) {
|
|
isViewColumnsLoading.value = true
|
|
try {
|
|
await loadViewColumns()
|
|
} catch (e) {
|
|
console.error(e)
|
|
}
|
|
isViewColumnsLoading.value = false
|
|
}
|
|
},
|
|
{ immediate: true },
|
|
)
|
|
|
|
const resizingColOldWith = ref('180px')
|
|
|
|
const updateGridViewColumn = async (id: string, props: Partial<GridColumnReqType>, undo = false) => {
|
|
if (!undo) {
|
|
const oldProps = Object.keys(props).reduce<Partial<GridColumnReqType>>((o: any, k) => {
|
|
if (gridViewCols.value[id][k as keyof GridColumnType]) {
|
|
if (k === 'width') o[k] = `${resizingColOldWith.value}px`
|
|
else o[k] = gridViewCols.value[id][k as keyof GridColumnType]
|
|
}
|
|
return o
|
|
}, {})
|
|
addUndo({
|
|
redo: {
|
|
fn: (w: Partial<GridColumnReqType>) => updateGridViewColumn(id, w, true),
|
|
args: [props],
|
|
},
|
|
undo: {
|
|
fn: (w: Partial<GridColumnReqType>) => updateGridViewColumn(id, w, true),
|
|
args: [oldProps],
|
|
},
|
|
scope: defineViewScope({ view: view.value }),
|
|
})
|
|
}
|
|
try {
|
|
// sync with server if allowed
|
|
if (!isPublic && canEditViewFields.value && gridViewCols.value[id]?.id) {
|
|
const colId = gridViewCols.value[id].id
|
|
|
|
// Route to the correct backend operation based on view type
|
|
const operationParams =
|
|
view.value?.type === ViewTypes.TIMELINE
|
|
? { operation: 'timelineColumnUpdate' as const, timelineViewColumnId: colId }
|
|
: view.value?.type === ViewTypes.LIST
|
|
? { operation: 'listColumnUpdate' as const, listViewColumnId: colId }
|
|
: { operation: 'gridColumnUpdate' as const, gridViewColumnId: colId }
|
|
|
|
await $api.internal.postOperation(view.value!.fk_workspace_id!, view.value!.base_id!, operationParams, props)
|
|
}
|
|
|
|
if (gridViewCols.value?.[id]) {
|
|
Object.assign(gridViewCols.value[id], {
|
|
...gridViewCols.value[id],
|
|
...props,
|
|
})
|
|
} else {
|
|
// fallback to reload
|
|
await loadViewColumns()
|
|
}
|
|
} catch (e) {
|
|
// this could happen if user doesn't have permission to update view columns
|
|
// todo: find out root cause and handle with isUIAllowed
|
|
console.error(e)
|
|
}
|
|
}
|
|
|
|
watch(
|
|
sortedAndFilteredFields,
|
|
(v) => {
|
|
if (rootFields) rootFields.value = v || []
|
|
},
|
|
{ immediate: true },
|
|
)
|
|
|
|
const evtListener = (evt: string, payload: any) => {
|
|
if (payload.fk_view_id !== view.value?.id) return
|
|
|
|
if (evt === 'view_column_update') {
|
|
const col = gridViewCols.value?.[payload.fk_column_id]
|
|
if (col) {
|
|
const reloadNeeded = payload?.group_by !== col?.group_by || (!col.show && payload?.show)
|
|
|
|
Object.assign(col, payload)
|
|
|
|
const field = fields.value?.find((f) => f.fk_column_id === payload.fk_column_id)
|
|
if (field) {
|
|
const currentColumnField = col || {}
|
|
Object.assign(field, {
|
|
show: currentColumnField.show || isColumnViewEssential(currentColumnField),
|
|
order: currentColumnField.order || order++,
|
|
aggregation: currentColumnField?.aggregation ?? CommonAggregations.None,
|
|
})
|
|
|
|
fields.value?.sort((a: Field, b: Field) => a.order - b.order)
|
|
}
|
|
|
|
if (reloadNeeded) {
|
|
nextTick(() => reloadData?.({ shouldShowLoading: false }))
|
|
}
|
|
|
|
$eventBus.smartsheetStoreEventBus.emit(SmartsheetStoreEvents.TRIGGER_RE_RENDER)
|
|
}
|
|
} else if (evt === 'view_column_refresh') {
|
|
loadViewColumns()
|
|
nextTick(() => reloadData?.({ shouldShowLoading: false }))
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
$eventBus.realtimeViewMetaEventBus.on(evtListener)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
$eventBus.realtimeViewMetaEventBus.off(evtListener)
|
|
})
|
|
|
|
provide(FieldsInj, rootFields)
|
|
|
|
return {
|
|
fields,
|
|
fieldsMap,
|
|
loadViewColumns,
|
|
filteredFieldList,
|
|
searchBasisIdMap,
|
|
numberOfHiddenFields,
|
|
filterQuery,
|
|
showAll,
|
|
hideAll,
|
|
saveOrUpdate,
|
|
sortedAndFilteredFields,
|
|
showSystemFields,
|
|
metaColumnById,
|
|
toggleFieldVisibility,
|
|
toggleFieldStyles,
|
|
isViewColumnsLoading,
|
|
updateGridViewColumn,
|
|
gridViewCols,
|
|
resizingColOldWith,
|
|
isLocalMode,
|
|
updateDefaultViewColumnMeta,
|
|
hidingViewColumnsMap,
|
|
hasViewFieldDataEditPermission,
|
|
canUpdateViewMeta,
|
|
}
|
|
},
|
|
'useViewColumnsOrThrow',
|
|
)
|
|
|
|
export { useProvideViewColumns }
|
|
|
|
export function useViewColumnsOrThrow() {
|
|
const viewColumns = useViewColumns()
|
|
if (viewColumns == null) throw new Error('Please call `useProvideViewColumns` on the appropriate parent component')
|
|
return viewColumns
|
|
}
|