Browse Source

Merge pull request #2882 from nocodb/feat/gui-v2-column-width

feat(gui-v2): add column resize option in grid view
pull/2919/head
Pranav C 2 years ago committed by GitHub
parent
commit
10ed6a62ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 40
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  2. 66
      packages/nc-gui-v2/composables/useGridViewColumnWidth.ts
  3. 8
      packages/nc-gui-v2/composables/useUIPermission/index.ts
  4. 4
      packages/nc-gui-v2/composables/useUIPermission/rolePermissions.ts
  5. 59
      packages/nc-gui-v2/plugins/resizeDirective.ts

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

@ -1,7 +1,7 @@
<script lang="ts" setup>
import { computed } from '@vue/reactivity'
import { ColumnType, isVirtualCol } from 'nocodb-sdk'
import { inject, onKeyStroke, onMounted, provide } from '#imports'
import { isVirtualCol } from 'nocodb-sdk'
import { getCurrentInstance, inject, onKeyStroke, onMounted, provide } from '#imports'
import useGridViewColumnWidth from '~/composables/useGridViewColumnWidth'
import {
ActiveViewInj,
ChangePageInj,
@ -27,6 +27,8 @@ const selected = reactive<{ row?: number | null; col?: number | null }>({})
const editEnabled = ref(false)
const { loadData, paginationData, formattedData: data, updateRowProperty, changePage } = useViewData(meta, view)
const { loadGridViewColumns, updateWidth, resizingColWidth, resizingCol } = useGridViewColumnWidth(view)
onMounted(loadGridViewColumns)
provide(IsFormInj, false)
provide(IsGridInj, true)
@ -59,6 +61,14 @@ watch(
{ 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({
loadData,
})
@ -71,7 +81,15 @@ defineExpose({
<thead>
<tr>
<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.id"
@xcresize="onresize(col.id, $event)"
@xcresizing="onXcResizing(col.title, $event)"
@xcresized="resizingCol = null"
>
<SmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" />
<SmartsheetHeaderCell v-else :column="col" />
</th>
@ -94,7 +112,7 @@ defineExpose({
// 'text-center': isCentrallyAligned(columnObj),
// 'required': isRequired(columnObj, rowObj),
}"
:data-col="columnObj.title"
:data-col="columnObj.id"
@click="selectCell(rowIndex, colIndex)"
@dblclick="editEnabled = true"
>
@ -204,7 +222,6 @@ defineExpose({
min-height: 31px !important;
position: relative;
padding: 0 5px !important;
min-width: 200px;
}
table,
@ -220,6 +237,7 @@ defineExpose({
}
td {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@ -246,4 +264,14 @@ defineExpose({
opacity: 0.1;
}
}
:deep {
.resizer:hover,
.resizer:active,
.resizer:focus {
// todo: replace with primary color
@apply bg-blue-500/50;
cursor: col-resize;
}
}
</style>

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

@ -0,0 +1,66 @@
import { useStyleTag } from '@vueuse/core'
import type { ColumnType, GridColumnType, GridType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import useMetas from '~/composables/useMetas'
import useUIPermission from '~/composables/useUIPermission'
// todo: update swagger
export default (view: Ref<(GridType & { id?: string }) | undefined>) => {
const { css, load: loadCss, unload: unloadCss } = useStyleTag('')
const { isUIAllowed } = useUIPermission()
const { $api } = useNuxtApp()
const { metas } = useMetas()
const gridViewCols = ref<Record<string, GridColumnType>>({})
const resizingCol = ref('')
const resizingColWidth = ref('200px')
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 += `[data-col="${c.id}"]{min-width:${val};max-width:${val};width: ${val};}`
} else {
style += `[data-col="${c.id}"]{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
}
// sync with server if allowed
if (isUIAllowed('gridColUpdate') && gridViewCols.value[id]?.id) {
$api.dbView.gridColumnUpdate(gridViewCols.value[id].id as string, {
width,
})
}
}
return { loadGridViewColumns, updateWidth, resizingCol, resizingColWidth, loadCss, unloadCss }
}

8
packages/nc-gui-v2/composables/useUIPermission/index.ts

@ -6,7 +6,7 @@ export default () => {
const { $state } = useNuxtApp()
const projectRoles = useState<Record<string, boolean>>(USER_PROJECT_ROLES, () => ({}))
const isUIAllowed = (permission: keyof typeof rolePermissions[keyof typeof rolePermissions], _skipPreviewAs = false) => {
const isUIAllowed = (permission: string, _skipPreviewAs = false) => {
const user = $state.user
let userRoles = user?.value?.roles || {}
@ -32,11 +32,7 @@ export default () => {
// }
return Object.entries(roles).some(([role, hasRole]) => {
return (
hasRole &&
(rolePermissions[role as keyof typeof rolePermissions] === '*' ||
rolePermissions[role as keyof typeof rolePermissions]?.[permission])
)
return hasRole && (rolePermissions[role] === '*' || (rolePermissions[role] as Record<string, boolean>)?.[permission])
})
}

4
packages/nc-gui-v2/composables/useUIPermission/rolePermissions.ts

@ -1,4 +1,4 @@
export default {
const permissions: Record<string, Record<string, boolean> | '*'> = {
creator: '*',
owner: '*',
guest: {},
@ -35,3 +35,5 @@ export default {
projectSettings: true,
},
}
export default permissions

59
packages/nc-gui-v2/plugins/resizeDirective.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