mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
7.3 KiB
241 lines
7.3 KiB
<script lang="ts" setup> |
|
import { type ViewType } from 'nocodb-sdk' |
|
import { ViewTypes } from 'nocodb-sdk' |
|
|
|
const props = defineProps<{ |
|
// Prop used to align the dropdown to the left in sidebar |
|
alignLeftLevel: number | undefined |
|
source: Source |
|
}>() |
|
|
|
const { $e } = useNuxtApp() |
|
|
|
const alignLeftLevel = toRef(props, 'alignLeftLevel') |
|
|
|
const { refreshCommandPalette } = useCommandPalette() |
|
const viewsStore = useViewsStore() |
|
const { loadViews, navigateToView } = viewsStore |
|
|
|
const { isFeatureEnabled } = useBetaFeatureToggle() |
|
|
|
const table = inject(SidebarTableInj)! |
|
const base = inject(ProjectInj)! |
|
|
|
const isViewListLoading = ref(false) |
|
const toBeCreateType = ref<ViewTypes | 'AI'>() |
|
|
|
const isOpen = ref(false) |
|
|
|
const overlayClassName = computed(() => { |
|
if (alignLeftLevel.value === 1) return 'nc-view-create-dropdown nc-view-create-dropdown-left-1' |
|
|
|
if (alignLeftLevel.value === 2) return 'nc-view-create-dropdown nc-view-create-dropdown-left-2' |
|
|
|
return 'nc-view-create-dropdown' |
|
}) |
|
|
|
/** |
|
* Opens a modal for creating or editing a view. |
|
* |
|
* @param options - The options for opening the modal. |
|
* @param options.title - The title of the modal. Default is an empty string. |
|
* @param options.type - The type of view to create or edit. |
|
* @param options.copyViewId - The ID of the view to copy, if creating a copy. |
|
* @param options.groupingFieldColumnId - The ID of the column to use for grouping, if applicable. |
|
* @param options.calendarRange - The date range for calendar views. |
|
* @param options.coverImageColumnId - The ID of the column to use for cover images, if applicable. |
|
* |
|
* @returns A Promise that resolves when the modal operation is complete. |
|
* |
|
* @remarks |
|
* This function opens a modal dialog for creating or editing a view. |
|
* It handles the dialog state, view creation, and navigation to the newly created view. |
|
* After creating a view, it refreshes the command palette and reloads the views. |
|
* |
|
* @see {@link packages/nc-gui/components/smartsheet/topbar/ViewListDropdown.vue} for a similar implementation of view creation dialog. |
|
* If this function is updated, consider updating the other implementations as well. |
|
*/ |
|
async function onOpenModal({ |
|
title = '', |
|
type, |
|
copyViewId, |
|
groupingFieldColumnId, |
|
calendarRange, |
|
coverImageColumnId, |
|
}: { |
|
title?: string |
|
type: ViewTypes | 'AI' |
|
copyViewId?: string |
|
groupingFieldColumnId?: string |
|
calendarRange?: Array<{ |
|
fk_from_column_id: string |
|
fk_to_column_id: string | null // for ee only |
|
}> |
|
coverImageColumnId?: string |
|
}) { |
|
if (isViewListLoading.value) return |
|
|
|
toBeCreateType.value = type |
|
|
|
isViewListLoading.value = true |
|
await loadViews({ |
|
tableId: table.value.id!, |
|
}) |
|
|
|
isOpen.value = false |
|
isViewListLoading.value = false |
|
|
|
const isDlgOpen = ref(true) |
|
|
|
const { close } = useDialog(resolveComponent('DlgViewCreate'), { |
|
'modelValue': isDlgOpen, |
|
title, |
|
type, |
|
'tableId': table.value.id, |
|
'selectedViewId': copyViewId, |
|
calendarRange, |
|
groupingFieldColumnId, |
|
coverImageColumnId, |
|
'baseId': base.value.id, |
|
'onUpdate:modelValue': closeDialog, |
|
'onCreated': async (view?: ViewType) => { |
|
closeDialog() |
|
|
|
refreshCommandPalette() |
|
|
|
await loadViews({ |
|
tableId: table.value.id!, |
|
force: true, |
|
}) |
|
|
|
table.value.meta = { |
|
...(table.value.meta as object), |
|
hasNonDefaultViews: true, |
|
} |
|
|
|
if (view) { |
|
navigateToView({ |
|
view, |
|
tableId: table.value.id!, |
|
baseId: base.value.id!, |
|
doNotSwitchTab: true, |
|
}) |
|
} |
|
|
|
$e('a:view:create', { view: view?.type || type }) |
|
}, |
|
}) |
|
|
|
function closeDialog() { |
|
isOpen.value = false |
|
isDlgOpen.value = false |
|
|
|
close(1000) |
|
} |
|
} |
|
</script> |
|
|
|
<template> |
|
<NcDropdown v-model:visible="isOpen" :overlay-class-name="overlayClassName" destroy-popup-on-hide @click.stop="isOpen = true"> |
|
<slot /> |
|
<template #overlay> |
|
<NcMenu class="max-w-48"> |
|
<NcMenuItem @click.stop="onOpenModal({ type: ViewTypes.GRID })"> |
|
<div class="item" data-testid="sidebar-view-create-grid"> |
|
<div class="item-inner"> |
|
<GeneralViewIcon :meta="{ type: ViewTypes.GRID }" /> |
|
<div>Grid</div> |
|
</div> |
|
|
|
<GeneralLoader v-if="toBeCreateType === ViewTypes.GRID && isViewListLoading" /> |
|
<GeneralIcon v-else class="plus" icon="plus" /> |
|
</div> |
|
</NcMenuItem> |
|
|
|
<NcMenuItem v-if="!source.is_data_readonly" @click="onOpenModal({ type: ViewTypes.FORM })"> |
|
<div class="item" data-testid="sidebar-view-create-form"> |
|
<div class="item-inner"> |
|
<GeneralViewIcon :meta="{ type: ViewTypes.FORM }" /> |
|
<div>Form</div> |
|
</div> |
|
|
|
<GeneralLoader v-if="toBeCreateType === ViewTypes.FORM && isViewListLoading" /> |
|
<GeneralIcon v-else class="plus" icon="plus" /> |
|
</div> |
|
</NcMenuItem> |
|
<NcMenuItem @click="onOpenModal({ type: ViewTypes.GALLERY })"> |
|
<div class="item" data-testid="sidebar-view-create-gallery"> |
|
<div class="item-inner"> |
|
<GeneralViewIcon :meta="{ type: ViewTypes.GALLERY }" /> |
|
<div>Gallery</div> |
|
</div> |
|
|
|
<GeneralLoader v-if="toBeCreateType === ViewTypes.GALLERY && isViewListLoading" /> |
|
<GeneralIcon v-else class="plus" icon="plus" /> |
|
</div> |
|
</NcMenuItem> |
|
<NcMenuItem data-testid="sidebar-view-create-kanban" @click="onOpenModal({ type: ViewTypes.KANBAN })"> |
|
<div class="item"> |
|
<div class="item-inner"> |
|
<GeneralViewIcon :meta="{ type: ViewTypes.KANBAN }" /> |
|
<div>Kanban</div> |
|
</div> |
|
|
|
<GeneralLoader v-if="toBeCreateType === ViewTypes.KANBAN && isViewListLoading" /> |
|
<GeneralIcon v-else class="plus" icon="plus" /> |
|
</div> |
|
</NcMenuItem> |
|
<NcMenuItem data-testid="sidebar-view-create-calendar" @click="onOpenModal({ type: ViewTypes.CALENDAR })"> |
|
<div class="item"> |
|
<div class="item-inner"> |
|
<GeneralViewIcon :meta="{ type: ViewTypes.CALENDAR }" class="!w-4 !h-4" /> |
|
<div>{{ $t('objects.viewType.calendar') }}</div> |
|
</div> |
|
|
|
<GeneralLoader v-if="toBeCreateType === ViewTypes.CALENDAR && isViewListLoading" /> |
|
<GeneralIcon v-else class="plus" icon="plus" /> |
|
</div> |
|
</NcMenuItem> |
|
<template v-if="isFeatureEnabled(FEATURE_FLAG.AI_FEATURES)"> |
|
<NcDivider /> |
|
<NcMenuItem data-testid="sidebar-view-create-ai" @click="onOpenModal({ type: 'AI' })"> |
|
<div class="item"> |
|
<div class="item-inner"> |
|
<GeneralIcon icon="ncAutoAwesome" class="!w-4 !h-4 text-nc-fill-purple-dark" /> |
|
<div>{{ $t('labels.aiSuggested') }}</div> |
|
</div> |
|
</div> |
|
</NcMenuItem> |
|
</template> |
|
</NcMenu> |
|
</template> |
|
</NcDropdown> |
|
</template> |
|
|
|
<style lang="scss" scoped> |
|
.item { |
|
@apply flex flex-row items-center w-36 justify-between; |
|
} |
|
|
|
.item-inner { |
|
@apply flex flex-row items-center gap-x-1.75; |
|
} |
|
|
|
.plus { |
|
@apply text-gray-500; |
|
} |
|
</style> |
|
|
|
<style lang="scss"> |
|
.nc-view-create-dropdown { |
|
@apply !max-w-43 !min-w-43; |
|
} |
|
|
|
.nc-view-create-dropdown-left-1 { |
|
@apply !left-18; |
|
} |
|
|
|
.nc-view-create-dropdown-left-2 { |
|
@apply !left-23.5; |
|
} |
|
</style>
|
|
|