Browse Source

feat(gui-v2): add column resize in grid view

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/2882/head
Pranav C 2 years ago
parent
commit
636fb89c15
  1. 1
      packages/nc-gui-v2/components.d.ts
  2. 44
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  3. 58
      packages/nc-gui-v2/composables/useGridViewColumnWidth.ts
  4. 59
      packages/nc-gui-v2/plugins/directives.ts

1
packages/nc-gui-v2/components.d.ts vendored

@ -7,6 +7,7 @@ export {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
export interface GlobalComponents { export interface GlobalComponents {
AAlert: typeof import('ant-design-vue/es')['Alert']
AAnchorLink: typeof import('ant-design-vue/es')['AnchorLink'] AAnchorLink: typeof import('ant-design-vue/es')['AnchorLink']
AAutoComplete: typeof import('ant-design-vue/es')['AutoComplete'] AAutoComplete: typeof import('ant-design-vue/es')['AutoComplete']
AButton: typeof import('ant-design-vue/es')['Button'] AButton: typeof import('ant-design-vue/es')['Button']

44
packages/nc-gui-v2/components/smartsheet/Grid.vue

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from '@vue/reactivity' import { isVirtualCol } from 'nocodb-sdk'
import { ColumnType, isVirtualCol } from 'nocodb-sdk' import { getCurrentInstance, inject, onKeyStroke, onMounted, provide } from '#imports'
import { inject, onKeyStroke, onMounted, provide } from '#imports' import useGridViewColumnWidth from '~/composables/useGridViewColumnWidth'
import { import {
ActiveViewInj, ActiveViewInj,
ChangePageInj, ChangePageInj,
@ -26,7 +26,15 @@ const isPublicView = false
const selected = reactive<{ row?: number | null; col?: number | null }>({}) const selected = reactive<{ row?: number | null; col?: number | null }>({})
const editEnabled = ref(false) const editEnabled = ref(false)
const instance: any = getCurrentInstance()
const { loadData, paginationData, formattedData: data, updateRowProperty, changePage } = useViewData(meta, view) const { loadData, paginationData, formattedData: data, updateRowProperty, changePage } = useViewData(meta, view)
console.log(instance)
const { loadGridViewColumns, updateWidth, resizingColWidth, resizingCol } = useGridViewColumnWidth(
view,
instance.ctx.$options.__scopeId,
)
onMounted(loadGridViewColumns)
provide(IsFormInj, false) provide(IsFormInj, false)
provide(IsGridInj, true) provide(IsGridInj, true)
@ -59,6 +67,14 @@ watch(
{ immediate: true }, { immediate: true },
) )
const onresize = (colID: string, event: any) => {
updateWidth(colID, event.detail)
}
const onXcResizing = (cn: string, event: any) => {
resizingCol.value = cn
resizingColWidth.value = event.detail
}
defineExpose({ defineExpose({
loadData, loadData,
}) })
@ -71,7 +87,15 @@ defineExpose({
<thead> <thead>
<tr> <tr>
<th>#</th> <th>#</th>
<th v-for="col in fields" :key="col.title"> <th
v-for="col in fields"
:key="col.title"
v-xc-ver-resize
:data-col="col.title"
@xcresize="onresize(col.id, $event)"
@xcresizing="onXcResizing(col.title, $event)"
@xcresized="resizingCol = null"
>
<SmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" /> <SmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" />
<SmartsheetHeaderCell v-else :column="col" /> <SmartsheetHeaderCell v-else :column="col" />
</th> </th>
@ -204,7 +228,6 @@ defineExpose({
min-height: 31px !important; min-height: 31px !important;
position: relative; position: relative;
padding: 0 5px !important; padding: 0 5px !important;
min-width: 200px;
} }
table, table,
@ -220,6 +243,7 @@ defineExpose({
} }
td { td {
overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
@ -246,4 +270,14 @@ defineExpose({
opacity: 0.1; opacity: 0.1;
} }
} }
:deep {
.resizer:hover,
.resizer:active,
.resizer:focus {
// todo: replace with primary color
@apply bg-blue-500/50;
cursor: col-resize;
}
}
</style> </style>

58
packages/nc-gui-v2/composables/useGridViewColumnWidth.ts

@ -0,0 +1,58 @@
import { useStyleTag } from '@vueuse/core'
import type { ColumnType, GridColumnType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import type GridView from '../../nocodb/src/lib/models/GridView'
import useMetas from '~/composables/useMetas'
// todo: update swagger
export default (view: Ref<(GridView & { id?: string }) | undefined>, scopeId: string) => {
const { css, load: loadCss, unload: unloadCss } = useStyleTag('')
const gridViewCols = ref<Record<string, GridColumnType>>({})
const resizingCol = ref('')
const resizingColWidth = ref('200px')
const { $api } = useNuxtApp()
const { metas } = useMetas()
const columns = computed<ColumnType[]>(() => metas?.value?.[(view?.value as any)?.fk_model_id as string]?.columns)
watch(
// todo : update type in swagger
() => [gridViewCols, resizingCol, resizingColWidth],
() => {
let style = ''
for (const c of columns?.value || []) {
const val = gridViewCols?.value?.[c?.id as string]?.width || '200px'
if (val && c.title !== resizingCol?.value) {
style += `[${scopeId}] [data-col="${c.title}"]{min-width:${val};max-width:${val};width: ${val};}`
} else {
style += `[${scopeId}] [data-col="${c.title}"]{min-width:${resizingColWidth?.value};max-width:${resizingColWidth?.value};width: ${resizingColWidth?.value};}`
}
}
css.value = style
},
{ deep: true, immediate: true },
)
const loadGridViewColumns = async () => {
if (!view.value?.id) return
const colsData: GridColumnType[] = await $api.dbView.gridColumnsList(view.value.id)
gridViewCols.value = colsData.reduce<Record<string, GridColumnType>>(
(o, col) => ({
...o,
[col.fk_column_id as string]: col,
}),
{},
)
loadCss()
}
const updateWidth = (id: string, width: string) => {
if (gridViewCols?.value?.[id]) gridViewCols.value[id].width = width
}
return { loadGridViewColumns, updateWidth, resizingCol, resizingColWidth, loadCss, unloadCss }
}

59
packages/nc-gui-v2/plugins/directives.ts

@ -0,0 +1,59 @@
import { defineNuxtPlugin } from '#app'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.directive('xc-ver-resize', {
created: (el: Element) => {
// create resizer element
const resizer = document.createElement('div')
resizer.className = 'resizer'
resizer.style.position = 'absolute'
resizer.style.height = '100%'
resizer.style.width = '3px'
resizer.style.top = '0'
resizer.style.right = '-1px'
resizer.style.zIndex = '999'
// add resizer to element
el.appendChild(resizer)
resizer.addEventListener('mousedown', initDrag, false)
const instance = getCurrentInstance()
const emit =
instance?.emit ??
((arg, data) => {
const event = new CustomEvent(arg, { detail: data })
;(<HTMLElement>el).dispatchEvent(event)
})
let startX: number
let startWidth: number
// bind event handlers
function initDrag(e: MouseEvent) {
document.body.style.cursor = 'col-resize'
startX = e.clientX
startWidth = parseInt(document.defaultView?.getComputedStyle(el)?.width || '0', 10)
document.documentElement.addEventListener('mousemove', doDrag, false)
document.documentElement.addEventListener('mouseup', stopDrag, false)
}
let width: number | string
// emit event on dragging
function doDrag(e: MouseEvent) {
width = `${startWidth + e.clientX - startX}px`
emit('xcresizing', width)
}
// remove handlers and emit events on drag end
function stopDrag() {
resizer.classList.remove('primary')
document.body.style.cursor = ''
document.documentElement.removeEventListener('mousemove', doDrag, false)
document.documentElement.removeEventListener('mouseup', stopDrag, false)
emit('xcresize', width)
emit('xcresized')
}
},
})
})
Loading…
Cancel
Save