Browse Source

Merge pull request #6561 from nocodb/nc-fix/expanded-form-lazy-loading

Expanded form lazy loading for mobile
pull/6567/head
Muhammed Mustafa 1 year ago committed by GitHub
parent
commit
cb9a85add8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      packages/nc-gui/components/cell/attachment/index.vue
  2. 2
      packages/nc-gui/components/project/AllTables.vue
  3. 10
      packages/nc-gui/components/smartsheet/column/LookupOptions.vue
  4. 97
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  5. 7
      packages/nc-gui/components/smartsheet/grid/Table.vue
  6. 2
      packages/nc-gui/components/virtual-cell/Links.vue
  7. 1
      packages/nc-gui/lib/acl.ts
  8. 4
      tests/playwright/tests/db/columns/columnLtarDragdrop.spec.ts

30
packages/nc-gui/components/cell/attachment/index.vue

@ -50,7 +50,9 @@ const isExpandedForm = inject(IsExpandedFormOpenInj, ref(false))
const { isSharedForm } = useSmartsheetStoreOrThrow()! const { isSharedForm } = useSmartsheetStoreOrThrow()!
const { getPossibleAttachmentSrc, openAttachment } = useAttachment() const { isMobileMode } = useGlobal()
const { getPossibleAttachmentSrc, openAttachment: _openAttachment } = useAttachment()
const { const {
isPublic, isPublic,
@ -61,7 +63,7 @@ const {
visibleItems, visibleItems,
onDrop, onDrop,
isLoading, isLoading,
open, open: _open,
FileIcon, FileIcon,
selectedImage, selectedImage,
isReadonly: _isReadonly, isReadonly: _isReadonly,
@ -136,7 +138,7 @@ watch(
useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e) => { useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e) => {
if (e.key === 'Enter' && !isReadonly.value) { if (e.key === 'Enter' && !isReadonly.value) {
e.stopPropagation() e.stopPropagation()
if (!modalVisible.value) { if (!modalVisible.value && !isMobileMode.value) {
modalVisible.value = true modalVisible.value = true
} else { } else {
// click Attach File button // click Attach File button
@ -146,6 +148,24 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e) => {
}) })
const rowHeight = inject(RowHeightInj, ref()) const rowHeight = inject(RowHeightInj, ref())
const open = () => {
if (isMobileMode.value) return (isExpandedForm.value = true)
_open()
}
const openAttachment = (item: any) => {
if (isMobileMode.value) return
_openAttachment(item)
}
const onExpand = () => {
if (isMobileMode.value) return
modalVisible.value = true
}
</script> </script>
<template> <template>
@ -220,7 +240,7 @@ const rowHeight = inject(RowHeightInj, ref())
:class="{ 'ml-2': active }" :class="{ 'ml-2': active }"
@click=" @click="
() => { () => {
if (isGallery) return if (isGallery || isMobileMode) return
selectedImage = item selectedImage = item
} }
" "
@ -264,7 +284,7 @@ const rowHeight = inject(RowHeightInj, ref())
<component <component
:is="iconMap.expand" :is="iconMap.expand"
class="transform dark:(!text-white) group-hover:(!text-grey-800 scale-120) text-gray-500 text-[0.75rem]" class="transform dark:(!text-white) group-hover:(!text-grey-800 scale-120) text-gray-500 text-[0.75rem]"
@click.stop="modalVisible = true" @click.stop="onExpand"
/> />
</NcTooltip> </NcTooltip>
</div> </div>

2
packages/nc-gui/components/project/AllTables.vue

@ -9,7 +9,7 @@ const { openedProject } = storeToRefs(useBases())
const isNewBaseModalOpen = ref(false) const isNewBaseModalOpen = ref(false)
const isDataSourceLimitReached = computed(() => Number(openedProject.value?.bases?.length) > 1) const isDataSourceLimitReached = computed(() => Number(openedProject.value?.sources?.length) > 1)
const { isUIAllowed } = useRoles() const { isUIAllowed } = useRoles()

10
packages/nc-gui/components/smartsheet/column/LookupOptions.vue

@ -16,6 +16,8 @@ const meta = inject(MetaInj, ref())
const { t } = useI18n() const { t } = useI18n()
const { appInfo } = useGlobal()
const { setAdditionalValidations, validateInfos, onDataTypeChange, isEdit } = useColumnCreateStoreOrThrow() const { setAdditionalValidations, validateInfos, onDataTypeChange, isEdit } = useColumnCreateStoreOrThrow()
const baseStore = useBase() const baseStore = useBase()
@ -37,7 +39,13 @@ const refTables = computed(() => {
} }
const _refTables = meta.value.columns const _refTables = meta.value.columns
.filter((column) => isLinksOrLTAR(column) && !column.system && column.source_id === meta.value?.source_id) .filter(
(column) =>
isLinksOrLTAR(column) &&
!column.system &&
column.source_id === meta.value?.source_id &&
(!appInfo.value.ee || (column?.colOptions as any)?.type === 'bt'),
)
.map((column) => ({ .map((column) => ({
col: column.colOptions, col: column.colOptions,
column, column,

97
packages/nc-gui/components/smartsheet/expanded-form/index.vue

@ -99,6 +99,8 @@ const isKanban = inject(IsKanbanInj, ref(false))
provide(MetaInj, meta) provide(MetaInj, meta)
const isLoading = ref(true)
const { const {
commentsDrawer, commentsDrawer,
changedColumns, changedColumns,
@ -117,22 +119,6 @@ const {
const duplicatingRowInProgress = ref(false) const duplicatingRowInProgress = ref(false)
if (props.loadRow) {
await _loadRow(rowId.value)
}
if (rowId.value) {
try {
await _loadRow(rowId.value)
} catch (e: any) {
if (e.response?.status === 404) {
// todo: i18n
message.error('Record not found')
router.replace({ query: {} })
} else throw e
}
}
useProvideSmartsheetStore(ref({}) as Ref<ViewType>, meta) useProvideSmartsheetStore(ref({}) as Ref<ViewType>, meta)
watch( watch(
@ -232,7 +218,26 @@ provide(IsExpandedFormOpenInj, isExpanded)
const cellWrapperEl = ref() const cellWrapperEl = ref()
onMounted(() => { onMounted(async () => {
isLoading.value = true
if (props.loadRow) {
await _loadRow()
}
if (props.rowId) {
try {
await _loadRow(props.rowId)
} catch (e: any) {
if (e.response?.status === 404) {
// todo: i18n
message.error('Record not found')
router.replace({ query: {} })
} else throw e
}
}
isLoading.value = false
setTimeout(() => { setTimeout(() => {
cellWrapperEl.value?.$el?.querySelector('input,select,textarea')?.focus() cellWrapperEl.value?.$el?.querySelector('input,select,textarea')?.focus()
}, 300) }, 300)
@ -389,10 +394,10 @@ export default {
<MdiChevronDown class="text-md" /> <MdiChevronDown class="text-md" />
</NcButton> </NcButton>
</div> </div>
<div <div v-if="isLoading">
v-if="displayValue && !row.rowMeta?.new" <a-skeleton-input class="!h-8 !sm:mr-14 !w-52 mt-1 !rounded-md !overflow-hidden" active size="small" />
class="flex items-center truncate w-32 hover:w-64 transition-all font-bold text-gray-800 text-xl" </div>
> <div v-else-if="displayValue && !row.rowMeta?.new" class="flex items-center font-bold text-gray-800 text-xl w-64">
<span class="truncate"> <span class="truncate">
{{ displayValue }} {{ displayValue }}
</span> </span>
@ -495,7 +500,7 @@ export default {
:col-id="col.id" :col-id="col.id"
:data-testid="`nc-expand-col-${col.title}`" :data-testid="`nc-expand-col-${col.title}`"
> >
<div class="flex items-start flex-row xs:(flex-col w-full) nc-expanded-cell"> <div class="flex items-start flex-row xs:(flex-col w-full) nc-expanded-cell min-h-10">
<div class="w-[12rem] xs:(w-full) mt-1.5 !h-[35px]"> <div class="w-[12rem] xs:(w-full) mt-1.5 !h-[35px]">
<LazySmartsheetHeaderVirtualCell <LazySmartsheetHeaderVirtualCell
v-if="isVirtualCol(col)" v-if="isVirtualCol(col)"
@ -506,10 +511,23 @@ export default {
<LazySmartsheetHeaderCell v-else class="nc-expanded-cell-header !text-gray-600" :column="col" /> <LazySmartsheetHeaderCell v-else class="nc-expanded-cell-header !text-gray-600" :column="col" />
</div> </div>
<LazySmartsheetDivDataCell <template v-if="isLoading">
<div
v-if="isMobileMode"
class="!h-8.5 !xs:h-12 !xs:bg-white !sm:mr-21 !w-60 mt-0.75 !rounded-lg !overflow-hidden"
></div>
<a-skeleton-input
v-else
class="!h-8.5 !xs:h-9.5 !xs:bg-white !sm:mr-21 !w-60 mt-0.75 !rounded-lg !overflow-hidden"
active
size="small"
/>
</template>
<template v-else>
<SmartsheetDivDataCell
v-if="col.title" v-if="col.title"
:ref="i ? null : (el: any) => (cellWrapperEl = el)" :ref="i ? null : (el: any) => (cellWrapperEl = el)"
class="!bg-white rounded-lg !w-[20rem] !xs:w-full border-1 overflow-hidden border-gray-200 px-1 min-h-[35px] flex items-center relative" class="!bg-white rounded-lg !w-[20rem] !xs:w-full border-1 border-gray-200 overflow-hidden px-1 min-h-[35px] flex items-center relative"
> >
<LazySmartsheetVirtualCell v-if="isVirtualCol(col)" v-model="_row.row[col.title]" :row="_row" :column="col" /> <LazySmartsheetVirtualCell v-if="isVirtualCol(col)" v-model="_row.row[col.title]" :row="_row" :column="col" />
@ -522,7 +540,8 @@ export default {
:read-only="isPublic" :read-only="isPublic"
@update:model-value="changedColumns.add(col.title)" @update:model-value="changedColumns.add(col.title)"
/> />
</LazySmartsheetDivDataCell> </SmartsheetDivDataCell>
</template>
</div> </div>
</div> </div>
<div v-if="hiddenFields.length > 0" class="flex w-full px-12 items-center py-3"> <div v-if="hiddenFields.length > 0" class="flex w-full px-12 items-center py-3">
@ -543,19 +562,37 @@ export default {
:class="`nc-expand-col-${col.title}`" :class="`nc-expand-col-${col.title}`"
:data-testid="`nc-expand-col-${col.title}`" :data-testid="`nc-expand-col-${col.title}`"
> >
<div class="flex flex-row items-start"> <div class="flex flex-row items-start min-h-10">
<div class="w-[12rem] scale-110 !h-[35px] mt-2.5"> <div class="w-[12rem] scale-110 !h-[35px] mt-2.5">
<LazySmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" class="!text-gray-600" :column="col" /> <LazySmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" class="!text-gray-600" />
<LazySmartsheetHeaderCell v-else class="!text-gray-600" :column="col" /> <LazySmartsheetHeaderCell v-else class="!text-gray-600" :column="col" />
</div> </div>
<template v-if="isLoading">
<div
v-if="isMobileMode"
class="!h-8.5 !xs:h-9.5 !xs:bg-white !sm:mr-21 !w-60 mt-0.75 !rounded-lg !overflow-hidden"
></div>
<a-skeleton-input
v-else
class="!h-8.5 !xs:h-12 !xs:bg-white !sm:mr-21 !w-60 mt-0.75 !rounded-lg !overflow-hidden"
active
size="small"
/>
</template>
<template v-else>
<LazySmartsheetDivDataCell <LazySmartsheetDivDataCell
v-if="col.title" v-if="col.title"
:ref="i ? null : (el: any) => (cellWrapperEl = el)" :ref="i ? null : (el: any) => (cellWrapperEl = el)"
class="!bg-white rounded-lg !w-[20rem] border-1 overflow-hidden border-gray-200 px-1 min-h-[35px] flex items-center relative" class="!bg-white rounded-lg !w-[20rem] border-1 overflow-hidden border-gray-200 px-1 min-h-[35px] flex items-center relative"
> >
<LazySmartsheetVirtualCell v-if="isVirtualCol(col)" v-model="_row.row[col.title]" :row="_row" :column="col" /> <LazySmartsheetVirtualCell
v-if="isVirtualCol(col)"
v-model="_row.row[col.title]"
:row="_row"
:column="col"
/>
<LazySmartsheetCell <LazySmartsheetCell
v-else v-else
@ -567,10 +604,12 @@ export default {
@update:model-value="changedColumns.add(col.title)" @update:model-value="changedColumns.add(col.title)"
/> />
</LazySmartsheetDivDataCell> </LazySmartsheetDivDataCell>
</template>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div <div
v-if="isUIAllowed('dataEdit')" v-if="isUIAllowed('dataEdit')"
class="w-full h-16 border-t-1 border-gray-200 bg-white flex items-center justify-end p-3 xs:(p-0 mt-4 border-t-0 gap-x-4 justify-between)" class="w-full h-16 border-t-1 border-gray-200 bg-white flex items-center justify-end p-3 xs:(p-0 mt-4 border-t-0 gap-x-4 justify-between)"
@ -633,7 +672,7 @@ export default {
class="nc-comments-drawer border-1 relative border-gray-200 w-1/3 max-w-125 bg-gray-50 rounded-xl min-w-0 overflow-hidden h-full xs:hidden" class="nc-comments-drawer border-1 relative border-gray-200 w-1/3 max-w-125 bg-gray-50 rounded-xl min-w-0 overflow-hidden h-full xs:hidden"
:class="{ active: commentsDrawer && isUIAllowed('commentList') }" :class="{ active: commentsDrawer && isUIAllowed('commentList') }"
> >
<LazySmartsheetExpandedFormComments /> <SmartsheetExpandedFormComments />
</div> </div>
</div> </div>
</div> </div>

7
packages/nc-gui/components/smartsheet/grid/Table.vue

@ -140,6 +140,7 @@ const { getMeta } = useMetas()
const { addUndo, clone, defineViewScope } = useUndoRedo() const { addUndo, clone, defineViewScope } = useUndoRedo()
const { const {
predictingNextColumn, predictingNextColumn,
predictedNextColumn, predictedNextColumn,
@ -1357,7 +1358,7 @@ const handleCellClick = (event: MouseEvent, row: number, col: number) => {
> >
<div class="items-center flex gap-1 min-w-[60px]"> <div class="items-center flex gap-1 min-w-[60px]">
<div <div
v-if="!readOnly || !isLocked" v-if="!readOnly || !isLocked || isMobileMode"
class="nc-row-no text-xs text-gray-500" class="nc-row-no text-xs text-gray-500"
:class="{ toggle: !readOnly, hidden: row.rowMeta.selected }" :class="{ toggle: !readOnly, hidden: row.rowMeta.selected }"
> >
@ -1864,7 +1865,7 @@ const handleCellClick = (event: MouseEvent, row: number, col: number) => {
.nc-grid-row { .nc-grid-row {
.nc-row-expand-and-checkbox { .nc-row-expand-and-checkbox {
@apply w-full items-center justify-between; @apply !xs:hidden w-full items-center justify-between;
} }
.nc-expand { .nc-expand {
@ -1887,7 +1888,7 @@ const handleCellClick = (event: MouseEvent, row: number, col: number) => {
} }
.nc-row-expand-and-checkbox { .nc-row-expand-and-checkbox {
@apply flex; @apply !xs:hidden flex;
} }
} }
} }

2
packages/nc-gui/components/virtual-cell/Links.vue

@ -109,7 +109,7 @@ const localCellValue = computed<any[]>(() => {
</div> </div>
<div class="flex-grow" /> <div class="flex-grow" />
<div v-if="!isLocked && !isUnderLookup" class="flex justify-end hidden group-hover:flex items-center"> <div v-if="!isLocked && !isUnderLookup" class="!xs:hidden flex justify-end hidden group-hover:flex items-center">
<MdiPlus <MdiPlus
v-if="(!readOnly && isUIAllowed('dataEdit')) || isForm" v-if="(!readOnly && isUIAllowed('dataEdit')) || isForm"
class="select-none !text-md text-gray-700 nc-action-icon nc-plus" class="select-none !text-md text-gray-700 nc-action-icon nc-plus"

1
packages/nc-gui/lib/acl.ts

@ -70,6 +70,7 @@ const rolePermissions = {
csvImport: true, csvImport: true,
baseRename: true, baseRename: true,
baseDuplicate: true, baseDuplicate: true,
sourceCreate: true,
}, },
}, },
[ProjectRoles.EDITOR]: { [ProjectRoles.EDITOR]: {

4
tests/playwright/tests/db/columns/columnLtarDragdrop.spec.ts

@ -4,6 +4,7 @@ import { Api, UITypes } from 'nocodb-sdk';
import { DashboardPage } from '../../../pages/Dashboard'; import { DashboardPage } from '../../../pages/Dashboard';
import { GridPage } from '../../../pages/Dashboard/Grid'; import { GridPage } from '../../../pages/Dashboard/Grid';
import { getTextExcludeIconText } from '../../../tests/utils/general'; import { getTextExcludeIconText } from '../../../tests/utils/general';
import { isEE } from '../../../setup/db';
let api: Api<any>; let api: Api<any>;
const recordCount = 10; const recordCount = 10;
@ -99,6 +100,9 @@ test.describe('Links', () => {
const linkField = await getTextExcludeIconText(columnAddModal.locator(`.ant-form-item-control-input`).nth(2)); const linkField = await getTextExcludeIconText(columnAddModal.locator(`.ant-form-item-control-input`).nth(2));
const childColumn = await getTextExcludeIconText(columnAddModal.locator(`.ant-form-item-control-input`).nth(3)); const childColumn = await getTextExcludeIconText(columnAddModal.locator(`.ant-form-item-control-input`).nth(3));
// TODO: Handle this in EE
if (isEE()) return;
// validate // validate
expect(columnType).toContain('Lookup'); expect(columnType).toContain('Lookup');
expect(linkField).toContain('Table1List'); expect(linkField).toContain('Table1List');

Loading…
Cancel
Save