Browse Source

Merge pull request #2992 from nocodb/fix/grid-view

fix: grid view corrections
pull/3002/head
Pranav C 2 years ago committed by GitHub
parent
commit
c2917d090b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      packages/nc-gui-v2/components/cell/Text.vue
  2. 1
      packages/nc-gui-v2/components/cell/TextArea.vue
  3. 25
      packages/nc-gui-v2/components/smartsheet/Cell.vue
  4. 56
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  5. 189
      packages/nc-gui-v2/components/smartsheet/VirtualCell.vue
  6. 2
      packages/nc-gui-v2/components/tabs/Smartsheet.vue
  7. 2
      packages/nc-gui-v2/composables/useColumnCreateStore.ts
  8. 2
      packages/nc-gui-v2/composables/useViewColumns.ts
  9. 2
      packages/nc-gui-v2/context/index.ts
  10. 5
      packages/nc-gui-v2/lib/enums.ts

2
packages/nc-gui-v2/components/cell/Text.vue

@ -18,6 +18,6 @@ const focus = (el: HTMLInputElement) => el?.focus()
</script> </script>
<template> <template>
<input v-if="editEnabled" :ref="focus" v-model="vModel" class="h-full w-full outline-none" /> <input v-if="editEnabled" :ref="focus" v-model="vModel" class="h-full w-full outline-none" @blur="editEnabled = false" />
<span v-else>{{ vModel }}</span> <span v-else>{{ vModel }}</span>
</template> </template>

1
packages/nc-gui-v2/components/cell/TextArea.vue

@ -24,6 +24,7 @@ const focus = (el: HTMLTextAreaElement) => el?.focus()
v-model="vModel" v-model="vModel"
rows="4" rows="4"
class="h-full w-full min-h-[60px] outline-none" class="h-full w-full min-h-[60px] outline-none"
@blur="editEnabled = false"
@keydown.alt.enter.stop @keydown.alt.enter.stop
@keydown.shift.enter.stop @keydown.shift.enter.stop
/> />

25
packages/nc-gui-v2/components/smartsheet/Cell.vue

@ -4,6 +4,7 @@ import type { ColumnType } from 'nocodb-sdk'
import { provide, toRef } from 'vue' import { provide, toRef } from 'vue'
import { computed, useColumn, useDebounceFn, useVModel } from '#imports' import { computed, useColumn, useDebounceFn, useVModel } from '#imports'
import { ColumnInj, EditModeInj } from '~/context' import { ColumnInj, EditModeInj } from '~/context'
import { NavigateDir } from '~/lib'
interface Props { interface Props {
column: ColumnType column: ColumnType
@ -17,14 +18,15 @@ interface Emits {
const { column, ...props } = defineProps<Props>() const { column, ...props } = defineProps<Props>()
const emit = defineEmits(['update:modelValue', 'save']) const emit = defineEmits(['update:modelValue', 'save', 'navigate', 'update:editEnabled'])
provide(ColumnInj, column) provide(ColumnInj, column)
provide(EditModeInj, toRef(props, 'editEnabled')) provide(EditModeInj, useVModel(props, 'editEnabled', emit))
let changed = $ref(false) let changed = $ref(false)
const syncValue = useDebounceFn(function () { const syncValue = useDebounceFn(function () {
changed = false
emit('save') emit('save')
}, 1000) }, 1000)
@ -59,6 +61,7 @@ const vModel = computed({
syncValue() syncValue()
} else if (!isManualSaved) { } else if (!isManualSaved) {
emit('save') emit('save')
changed = true
} }
} }
}, },
@ -87,10 +90,26 @@ const {
isPercent, isPercent,
isPhoneNumber, isPhoneNumber,
} = useColumn(column) } = useColumn(column)
const syncAndNavigate = (dir: NavigateDir) => {
if (changed) {
emit('save')
changed = false
}
emit('navigate', dir)
}
</script> </script>
<template> <template>
<div class="nc-cell" @keydown.stop.left @keydown.stop.right @keydown.stop.up @keydown.stop.down> <div
class="nc-cell"
@keydown.stop.left
@keydown.stop.right
@keydown.stop.up
@keydown.stop.down
@keydown.stop.enter.exact="syncAndNavigate(NavigateDir.NEXT)"
@keydown.stop.shift.enter.exact="syncAndNavigate(NavigateDir.PREV)"
>
<CellTextArea v-if="isTextArea" v-model="vModel" /> <CellTextArea v-if="isTextArea" v-model="vModel" />
<CellCheckbox v-else-if="isBoolean" v-model="vModel" /> <CellCheckbox v-else-if="isBoolean" v-model="vModel" />
<CellAttachment v-else-if="isAttachment" v-model="vModel" /> <CellAttachment v-else-if="isAttachment" v-model="vModel" />

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

@ -1,7 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { isVirtualCol } from 'nocodb-sdk' import { isVirtualCol } from 'nocodb-sdk'
import { import {
Row,
inject, inject,
onKeyStroke, onKeyStroke,
onMounted, onMounted,
@ -22,8 +21,9 @@ import {
PaginationDataInj, PaginationDataInj,
ReloadViewDataHookInj, ReloadViewDataHookInj,
} from '~/context' } from '~/context'
import MdiPlusIcon from '~icons/mdi/plus' import { NavigateDir } from '~/lib'
import MdiArrowExpandIcon from '~icons/mdi/arrow-expand' import MdiArrowExpandIcon from '~icons/mdi/arrow-expand'
import MdiPlusIcon from '~icons/mdi/plus'
const meta = inject(MetaInj) const meta = inject(MetaInj)
const view = inject(ActiveViewInj) const view = inject(ActiveViewInj)
@ -42,10 +42,7 @@ const addColumnDropdown = ref(false)
const contextMenu = ref(false) const contextMenu = ref(false)
const contextMenuTarget = ref(false) const contextMenuTarget = ref(false)
const visibleColLength = $computed(() => { const visibleColLength = $computed(() => fields.value?.length)
const cols = fields.value
return cols.filter((col) => !isVirtualCol(col)).length
})
const { const {
loadData, loadData,
@ -131,7 +128,7 @@ const clearCell = async (ctx: { row: number; col: number }) => {
/** handle keypress events */ /** handle keypress events */
onKeyStroke(['Tab', 'Shift', 'Enter', 'Delete', 'ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight'], async (e: KeyboardEvent) => { onKeyStroke(['Tab', 'Shift', 'Enter', 'Delete', 'ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight'], async (e: KeyboardEvent) => {
if (selected.row !== null && selected.col !== null) { if (selected.row === null || selected.col === null) return
/** on tab key press navigate through cells */ /** on tab key press navigate through cells */
switch (e.key) { switch (e.key) {
case 'Tab': case 'Tab':
@ -180,8 +177,29 @@ onKeyStroke(['Tab', 'Shift', 'Enter', 'Delete', 'ArrowDown', 'ArrowUp', 'ArrowLe
if (selected.row < data.value.length - 1) selected.row++ if (selected.row < data.value.length - 1) selected.row++
break break
} }
}
}) })
const onNavigate = (dir: NavigateDir) => {
if (selected.row === null || selected.col === null) return
switch (dir) {
case NavigateDir.NEXT:
if (selected.col < visibleColLength - 1) {
selected.col++
} else if (selected.row < data.value.length - 1) {
selected.row++
selected.col = 0
}
break
case NavigateDir.PREV:
if (selected.col > 0) {
selected.col--
} else if (selected.row > 0) {
selected.row--
selected.col = visibleColLength - 1
}
break
}
}
</script> </script>
<template> <template>
@ -255,15 +273,25 @@ onKeyStroke(['Tab', 'Shift', 'Enter', 'Delete', 'ArrowDown', 'ArrowUp', 'ArrowLe
@dblclick="editEnabled = true" @dblclick="editEnabled = true"
@contextmenu="contextMenuTarget = { row: rowIndex, col: colIndex }" @contextmenu="contextMenuTarget = { row: rowIndex, col: colIndex }"
> >
<SmartsheetVirtualCell v-if="isVirtualCol(columnObj)" v-model="row.row[columnObj.title]" :column="columnObj" /> <div class="w-full h-full">
<SmartsheetVirtualCell
v-if="isVirtualCol(columnObj)"
v-model="row.row[columnObj.title]"
:column="columnObj"
@navigate="onNavigate"
/>
<SmartsheetCell <SmartsheetCell
v-else v-else
v-model="row.row[columnObj.title]" v-model="row.row[columnObj.title]"
:column="columnObj" :column="columnObj"
:edit-enabled="editEnabled && selected.col === colIndex && selected.row === rowIndex" :edit-enabled="editEnabled && selected.col === colIndex && selected.row === rowIndex"
@update:edit-enabled="editEnabled = false"
@save="updateOrSaveRow(row, columnObj.title)" @save="updateOrSaveRow(row, columnObj.title)"
@navigate="onNavigate"
@cancel="editEnabled = false"
/> />
</div>
</td> </td>
</tr> </tr>
@ -318,13 +346,13 @@ onKeyStroke(['Tab', 'Shift', 'Enter', 'Delete', 'ArrowDown', 'ArrowUp', 'ArrowLe
min-height: 41px !important; min-height: 41px !important;
height: 41px !important; height: 41px !important;
position: relative; position: relative;
padding: 0 5px; //padding: 0 5px;
& > * { & > div {
overflow: hidden;
@apply flex align-center h-auto; @apply flex align-center h-auto;
padding: 0 5px;
} }
overflow: hidden;
} }
table, table,
@ -360,7 +388,7 @@ onKeyStroke(['Tab', 'Shift', 'Enter', 'Delete', 'ArrowDown', 'ArrowUp', 'ArrowLe
} }
td.active::before { td.active::before {
background: #0040bc /*var(--v-primary-base)*/; background: #0040bc;
opacity: 0.1; opacity: 0.1;
} }
} }

189
packages/nc-gui-v2/components/smartsheet/VirtualCell.vue

@ -2,6 +2,7 @@
import type { ColumnType } from 'nocodb-sdk' import type { ColumnType } from 'nocodb-sdk'
import { provide, useVirtualCell } from '#imports' import { provide, useVirtualCell } from '#imports'
import { ColumnInj } from '~/context' import { ColumnInj } from '~/context'
import { NavigateDir } from '~/lib'
interface Props { interface Props {
column: ColumnType column: ColumnType
@ -10,7 +11,7 @@ interface Props {
const { column, modelValue: value } = defineProps<Props>() const { column, modelValue: value } = defineProps<Props>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue', 'navigate'])
provide(ColumnInj, column) provide(ColumnInj, column)
provide('value', value) provide('value', value)
@ -19,196 +20,20 @@ const { isLookup, isBt, isRollup, isMm, isHm, isFormula, isCount } = useVirtualC
</script> </script>
<template> <template>
<div class="nc-virtual-cell"> <div
class="nc-virtual-cell"
@keydown.stop.enter.exact="emit('navigate', NavigateDir.NEXT)"
@keydown.stop.shift.enter.exact="emit('navigate', NavigateDir.PREV)"
>
<VirtualCellHasMany v-if="isHm" /> <VirtualCellHasMany v-if="isHm" />
<VirtualCellManyToMany v-else-if="isMm" /> <VirtualCellManyToMany v-else-if="isMm" />
<VirtualCellBelongsTo v-else-if="isBt" /> <VirtualCellBelongsTo v-else-if="isBt" />
<VirtualCellRollup v-else-if="isRollup" /> <VirtualCellRollup v-else-if="isRollup" />
<VirtualCellFormula v-else-if="isFormula" /> <VirtualCellFormula v-else-if="isFormula" />
<VirtualCellCount v-else-if="isCount" /> <VirtualCellCount v-else-if="isCount" />
<!-- <v-lazy> -->
<!-- <has-many-cell
v-if="hm"
ref="cell"
:row="row"
:value="row[column.title]"
:meta="meta"
:hm="hm"
:nodes="nodes"
:active="active"
:sql-ui="sqlUi"
:is-new="isNew"
:is-form="isForm"
:breadcrumbs="breadcrumbs"
:is-locked="isLocked"
:required="required"
:is-public="isPublic"
:metas="metas"
:column="column"
:password="password"
v-on="$listeners"
/>
<many-to-many-cell
v-else-if="mm"
ref="cell"
:is-public="isPublic"
:row="row"
:value="row[column.title]"
:meta="meta"
:nodes="nodes"
:sql-ui="sqlUi"
:active="active"
:is-new="isNew"
:api="api"
:is-form="isForm"
:breadcrumbs="breadcrumbs"
:is-locked="isLocked"
:required="required"
:column="column"
:metas="metas"
:password="password"
v-on="$listeners"
/>
<belongs-to-cell
v-else-if="bt"
ref="cell"
:is-public="isPublic"
:disabled-columns="disabledColumns"
:active="active"
:row="row"
:value="row[column.title]"
:meta="meta"
:nodes="nodes"
:api="api"
:sql-ui="sqlUi"
:is-new="isNew"
:is-form="isForm"
:breadcrumbs="breadcrumbs"
:is-locked="isLocked"
:metas="metas"
:column="column"
:password="password"
v-on="$listeners"
/>
<lookup-cell
v-else-if="lookup"
:disabled-columns="disabledColumns"
:active="active"
:row="row"
:value="row[column.title]"
:meta="meta"
:metas="metas"
:nodes="nodes"
:api="api"
:sql-ui="sqlUi"
:is-new="isNew"
:is-form="isForm"
:column="column"
:is-locked="isLocked"
v-on="$listeners "
/>
<formula-cell
v-else-if="formula"
:row="row"
:column="column"
:client="nodes.dbConnection.client"
/>
<rollup-cell
v-else-if="rollup"
:row="row"
:column="column"
/>
</v-lazy>
<span v-if="hint" class="nc-hint">{{ hint }}</span>
<div v-if="isLocked" class="nc-locked-overlay" /> -->
</div> </div>
</template> </template>
<!-- <script>
import { UITypes } from "nocodb-sdk";
import RollupCell from "./virtualCell/RollupCell";
import FormulaCell from "./virtualCell/FormulaCell";
import hasManyCell from "./virtualCell/HasManyCell";
import LookupCell from "./virtualCell/LookupCell";
import manyToManyCell from "./virtualCell/ManyToManyCell";
import belongsToCell from "./virtualCell/BelongsToCell";
// todo: optimize parent/child meta extraction
export default {
name: "VirtualCell",
components: {
RollupCell,
FormulaCell,
LookupCell,
belongsToCell,
manyToManyCell,
hasManyCell
},
props: {
breadcrumbs: {
type: Array,
default() {
return [];
}
},
column: [Object],
row: [Object],
nodes: [Object],
meta: [Object],
api: [Object, Function],
active: Boolean,
sqlUi: [Object, Function],
isNew: {
type: Boolean,
default: false
},
isForm: {
type: Boolean,
default: false
},
disabledColumns: Object,
hint: String,
isLocked: Boolean,
required: Boolean,
isPublic: Boolean,
metas: Object,
password: String
},
computed: {
hm() {
return this.column && this.column.uidt === UITypes.LinkToAnotherRecord && this.column.colOptions.type === "hm";
},
bt() {
return this.column && (this.column.uidt === UITypes.ForeignKey || this.column.uidt === UITypes.LinkToAnotherRecord) && this.column.colOptions.type === "bt";
},
mm() {
return this.column && this.column.uidt === UITypes.LinkToAnotherRecord && this.column.colOptions.type === "mm";
},
lookup() {
return this.column && this.column.uidt === UITypes.Lookup;
},
formula() {
return this.column && this.column.uidt === UITypes.Formula;
},
rollup() {
return this.column && this.column.uidt === UITypes.Rollup;
}
},
methods: {
async save(row) {
if (row && this.$refs.cell && this.$refs.cell.saveLocalState) {
try {
await this.$refs.cell.saveLocalState(row);
} catch (e) {
}
}
}
}
};
</script> -->
<style scoped> <style scoped>
.nc-hint { .nc-hint {
font-size: 0.61rem; font-size: 0.61rem;

2
packages/nc-gui-v2/components/tabs/Smartsheet.vue

@ -40,7 +40,7 @@ provide(RightSidebarInj, ref(true))
useProvideSmartsheetStore(activeView as Ref<TableType>, meta) useProvideSmartsheetStore(activeView as Ref<TableType>, meta)
watch(tabMeta, async (newTabMeta, oldTabMeta) => { watch(tabMeta, async (newTabMeta, oldTabMeta) => {
if (newTabMeta !== oldTabMeta && newTabMeta.id) await getMeta(newTabMeta.id) if (newTabMeta !== oldTabMeta && newTabMeta?.id) await getMeta(newTabMeta.id)
}) })
</script> </script>

2
packages/nc-gui-v2/composables/useColumnCreateStore.ts

@ -86,7 +86,7 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
// actions // actions
const generateNewColumnMeta = () => { const generateNewColumnMeta = () => {
setAdditionalValidations({}) setAdditionalValidations({})
formState.value = { meta: {}, ...sqlUi.value.getNewColumn((meta.value.columns?.length || 0) + 1) } formState.value = { meta: {}, ...sqlUi.value.getNewColumn((meta.value?.columns?.length || 0) + 1) }
} }
const onUidtOrIdTypeChange = () => { const onUidtOrIdTypeChange = () => {

2
packages/nc-gui-v2/composables/useViewColumns.ts

@ -40,7 +40,7 @@ export function useViewColumns(
} }
}, {}) }, {})
fields.value = meta.value.columns fields.value = meta.value?.columns
?.map((column) => { ?.map((column) => {
const currentColumnField = fieldById[column.id!] || {} const currentColumnField = fieldById[column.id!] || {}

2
packages/nc-gui-v2/context/index.ts

@ -21,4 +21,4 @@ export const FieldsInj: InjectionKey<Ref<any[]>> = Symbol('fields-injection')
export const ViewListInj: InjectionKey<Ref<(GridType | FormType | KanbanType | GalleryType)[]>> = Symbol('view-list-injection') export const ViewListInj: InjectionKey<Ref<(GridType | FormType | KanbanType | GalleryType)[]>> = Symbol('view-list-injection')
export const RightSidebarInj: InjectionKey<Ref<boolean>> = Symbol('right-sidebar-injection') export const RightSidebarInj: InjectionKey<Ref<boolean>> = Symbol('right-sidebar-injection')
export const EditModeInj: InjectionKey<Ref<boolean>> = Symbol('edit-mode-injection') export const EditModeInj: InjectionKey<ComputedRef<boolean>> = Symbol('edit-mode-injection')

5
packages/nc-gui-v2/lib/enums.ts

@ -48,3 +48,8 @@ export enum Language {
sl = 'Slovenščina', sl = 'Slovenščina',
pt_BR = 'Português (Brasil)', pt_BR = 'Português (Brasil)',
} }
export enum NavigateDir {
NEXT,
PREV,
}

Loading…
Cancel
Save