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.
292 lines
8.1 KiB
292 lines
8.1 KiB
<script setup lang="ts"> |
|
import { Pane, Splitpanes } from 'splitpanes' |
|
import 'splitpanes/dist/splitpanes.css' |
|
import type { ColumnType, LinkToAnotherRecordType, TableType } from 'nocodb-sdk' |
|
import { UITypes, isLinksOrLTAR } from 'nocodb-sdk' |
|
|
|
const props = defineProps<{ |
|
activeTab: TabItem |
|
}>() |
|
|
|
const { isUIAllowed } = useRoles() |
|
|
|
const { metas, getMeta } = useMetas() |
|
|
|
useSidebar('nc-right-sidebar') |
|
|
|
const { isMobileMode } = useGlobal() |
|
|
|
const activeTab = toRef(props, 'activeTab') |
|
|
|
const { isScriptsEnabled } = useScripts() |
|
|
|
const route = useRoute() |
|
|
|
const meta = computed<TableType | undefined>(() => { |
|
const viewId = route.params.viewId as string |
|
return viewId && metas.value[viewId] |
|
}) |
|
|
|
const { handleSidebarOpenOnMobileForNonViews } = useConfigStore() |
|
const { activeTableId } = storeToRefs(useTablesStore()) |
|
|
|
const { activeView, openedViewsTab, activeViewTitleOrId } = storeToRefs(useViewsStore()) |
|
const { isGallery, isGrid, isForm, isKanban, isLocked, isMap, isCalendar, xWhere } = useProvideSmartsheetStore(activeView, meta) |
|
|
|
useSqlEditor() |
|
|
|
const reloadViewDataEventHook = createEventHook() |
|
|
|
const reloadViewMetaEventHook = createEventHook<void | boolean>() |
|
|
|
const openNewRecordFormHook = createEventHook<void>() |
|
|
|
const { base } = storeToRefs(useBase()) |
|
|
|
const activeSource = computed(() => { |
|
return meta.value?.source_id && base.value && base.value.sources?.find((source) => source.id === meta.value?.source_id) |
|
}) |
|
|
|
useProvideKanbanViewStore(meta, activeView) |
|
useProvideMapViewStore(meta, activeView) |
|
useProvideCalendarViewStore(meta, activeView) |
|
|
|
// todo: move to store |
|
provide(MetaInj, meta) |
|
provide(ActiveViewInj, activeView) |
|
provide(IsLockedInj, isLocked) |
|
provide(ReloadViewDataHookInj, reloadViewDataEventHook) |
|
provide(ReloadViewMetaHookInj, reloadViewMetaEventHook) |
|
provide(OpenNewRecordFormHookInj, openNewRecordFormHook) |
|
provide(IsFormInj, isForm) |
|
provide(TabMetaInj, activeTab) |
|
provide(ActiveSourceInj, activeSource) |
|
provide(ReloadAggregateHookInj, createEventHook()) |
|
|
|
provide( |
|
ReadonlyInj, |
|
computed( |
|
() => |
|
!isUIAllowed('dataEdit', { |
|
skipSourceCheck: true, |
|
}), |
|
), |
|
) |
|
useExpandedFormDetachedProvider() |
|
|
|
useProvideViewColumns(activeView, meta, () => reloadViewDataEventHook?.trigger()) |
|
|
|
useProvideViewGroupBy(activeView, meta, xWhere) |
|
|
|
useProvideSmartsheetLtarHelpers(meta) |
|
|
|
const grid = ref() |
|
|
|
const extensionPaneRef = ref() |
|
|
|
const onDrop = async (event: DragEvent) => { |
|
event.preventDefault() |
|
try { |
|
// Access the dropped data |
|
const data = JSON.parse(event.dataTransfer!.getData('text/json') || '{}') |
|
// Do something with the received data |
|
|
|
// if dragged item is not from the same source, return |
|
if (data.sourceId !== meta.value?.source_id) return |
|
|
|
// if dragged item or opened view is not a table, return |
|
if (data.type !== 'table' || meta.value?.type !== 'table') return |
|
|
|
const childMeta = await getMeta(data.id) |
|
const parentMeta = metas.value[meta.value.id!] |
|
|
|
if (!childMeta || !parentMeta) return |
|
|
|
const parentPkCol = parentMeta.columns?.find((c) => c.pk) |
|
const childPkCol = childMeta.columns?.find((c) => c.pk) |
|
|
|
// if already a link column exists, create a new Lookup column |
|
const relationCol = parentMeta.columns?.find((c: ColumnType) => { |
|
if (!isLinksOrLTAR(c)) return false |
|
|
|
const ltarOptions = c.colOptions as LinkToAnotherRecordType |
|
|
|
if (ltarOptions.type !== 'mm') { |
|
return false |
|
} |
|
|
|
if (c.system) return false |
|
|
|
if (ltarOptions.fk_related_model_id === childMeta.id) { |
|
return true |
|
} |
|
|
|
return false |
|
}) |
|
|
|
if (relationCol) { |
|
const lookupCol = childMeta.columns?.find((c) => c.pv) ?? childMeta.columns?.[0] |
|
grid.value?.openColumnCreate({ |
|
uidt: UITypes.Lookup, |
|
title: `${data.title} Lookup`, |
|
fk_relation_column_id: relationCol.id, |
|
fk_lookup_column_id: lookupCol?.id, |
|
}) |
|
} else { |
|
if (!parentPkCol) { |
|
message.error('Parent table does not have a primary key column') |
|
return |
|
} |
|
|
|
if (!childPkCol) { |
|
message.error('Child table does not have a primary key column') |
|
return |
|
} |
|
|
|
grid.value?.openColumnCreate({ |
|
uidt: UITypes.Links, |
|
title: `${data.title}List`, |
|
parentId: parentMeta.id, |
|
childId: childMeta.id, |
|
parentTable: parentMeta.title, |
|
parentColumn: parentPkCol.title, |
|
childTable: childMeta.title, |
|
childColumn: childPkCol?.title, |
|
}) |
|
} |
|
} catch (e) { |
|
console.log('error', e) |
|
} |
|
} |
|
|
|
watch([activeViewTitleOrId, activeTableId], () => { |
|
handleSidebarOpenOnMobileForNonViews() |
|
}) |
|
|
|
const { leftSidebarWidth, windowSize } = storeToRefs(useSidebarStore()) |
|
|
|
const { isPanelExpanded, extensionPanelSize } = useExtensions() |
|
|
|
const contentSize = computed(() => { |
|
if (isPanelExpanded.value && extensionPanelSize.value) { |
|
return 100 - extensionPanelSize.value |
|
} else { |
|
return 100 |
|
} |
|
}) |
|
|
|
const contentMaxSize = computed(() => { |
|
if (!isPanelExpanded.value) { |
|
return 100 |
|
} else { |
|
return ((windowSize.value - leftSidebarWidth.value - 300) / (windowSize.value - leftSidebarWidth.value)) * 100 |
|
} |
|
}) |
|
|
|
const onResize = (sizes: { min: number; max: number; size: number }[]) => { |
|
if (sizes.length === 2) { |
|
if (!sizes[1].size) return |
|
extensionPanelSize.value = sizes[1].size |
|
} |
|
} |
|
|
|
const onReady = () => { |
|
if (isPanelExpanded.value && extensionPaneRef.value) { |
|
// wait until extension pane animation complete |
|
setTimeout(() => { |
|
extensionPaneRef.value?.onReady() |
|
}, 300) |
|
} |
|
} |
|
</script> |
|
|
|
<template> |
|
<div class="nc-container flex flex-col h-full" @drop="onDrop" @dragover.prevent> |
|
<LazySmartsheetTopbar /> |
|
<div style="height: calc(100% - var(--topbar-height))"> |
|
<Splitpanes |
|
v-if="openedViewsTab === 'view'" |
|
class="nc-extensions-content-resizable-wrapper" |
|
@ready="() => onReady()" |
|
@resized="onResize" |
|
> |
|
<Pane class="flex flex-col h-full min-w-0" :max-size="contentMaxSize" :size="contentSize"> |
|
<LazySmartsheetToolbar v-if="!isForm" /> |
|
<div |
|
:style="{ height: isForm || isMobileMode ? '100%' : 'calc(100% - var(--toolbar-height))' }" |
|
class="flex flex-row w-full" |
|
> |
|
<Transition name="layout" mode="out-in"> |
|
<div v-if="openedViewsTab === 'view'" class="flex flex-1 min-h-0 w-3/4"> |
|
<div class="h-full flex-1 min-w-0 min-h-0 bg-white"> |
|
<LazySmartsheetGrid v-if="isGrid || !meta || !activeView" ref="grid" /> |
|
|
|
<template v-if="activeView && meta"> |
|
<LazySmartsheetGallery v-if="isGallery" /> |
|
|
|
<LazySmartsheetForm v-else-if="isForm && !$route.query.reload" /> |
|
|
|
<LazySmartsheetKanban v-else-if="isKanban" /> |
|
|
|
<LazySmartsheetCalendar v-else-if="isCalendar" /> |
|
|
|
<LazySmartsheetMap v-else-if="isMap" /> |
|
</template> |
|
</div> |
|
</div> |
|
</Transition> |
|
</div> |
|
</Pane> |
|
<ExtensionsPane ref="extensionPaneRef" /> |
|
|
|
<Scripts :modal-visible="isScriptsEnabled" /> |
|
</Splitpanes> |
|
<SmartsheetDetails v-else /> |
|
</div> |
|
<LazySmartsheetExpandedFormDetached /> |
|
</div> |
|
</template> |
|
|
|
<style lang="scss"> |
|
:deep(.nc-right-sidebar.ant-layout-sider-collapsed) { |
|
@apply !w-0 !max-w-0 !min-w-0 overflow-x-hidden; |
|
} |
|
|
|
.nc-extensions-content-resizable-wrapper > { |
|
.splitpanes__splitter { |
|
@apply !w-0 relative overflow-visible z-40 -ml-1px; |
|
} |
|
.splitpanes__splitter:before { |
|
@apply bg-gray-200 absolute left-0 top-[12px] h-[calc(100%_-_24px)] rounded-full z-40; |
|
content: ''; |
|
} |
|
|
|
.splitpanes__splitter:hover:before { |
|
@apply bg-scrollbar; |
|
width: 3px !important; |
|
left: 0px; |
|
} |
|
|
|
.splitpanes--dragging .splitpanes__splitter:before { |
|
@apply bg-scrollbar; |
|
width: 3px !important; |
|
left: 0px; |
|
} |
|
|
|
.splitpanes--dragging .splitpanes__splitter { |
|
@apply w-1 mr-0; |
|
} |
|
} |
|
|
|
.splitpanes__pane { |
|
transition: width 0.15s ease-in-out !important; |
|
} |
|
|
|
.splitpanes--dragging { |
|
cursor: col-resize; |
|
|
|
> .splitpanes__pane { |
|
transition: none !important; |
|
} |
|
} |
|
</style>
|
|
|