Browse Source

Merge branch 'develop' into feat/gui-v2-formula-options

pull/2998/head
Wing-Kam Wong 2 years ago
parent
commit
e2c0ba8a4a
  1. 2
      packages/nc-gui-v2/components/cell/Text.vue
  2. 1
      packages/nc-gui-v2/components/cell/TextArea.vue
  3. 102
      packages/nc-gui-v2/components/smartsheet-column/CheckboxOptions.vue
  4. 1
      packages/nc-gui-v2/components/smartsheet-column/EditOrAdd.vue
  5. 25
      packages/nc-gui-v2/components/smartsheet/Cell.vue
  6. 162
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  7. 189
      packages/nc-gui-v2/components/smartsheet/VirtualCell.vue
  8. 2
      packages/nc-gui-v2/components/tabs/Smartsheet.vue
  9. 2
      packages/nc-gui-v2/composables/useColumnCreateStore.ts
  10. 2
      packages/nc-gui-v2/composables/useViewColumns.ts
  11. 2
      packages/nc-gui-v2/context/index.ts
  12. 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>
<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>
</template>

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

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

102
packages/nc-gui-v2/components/smartsheet-column/CheckboxOptions.vue

@ -0,0 +1,102 @@
<script setup lang="ts">
import { Sketch } from '@ckpack/vue-color'
import { useColumnCreateStoreOrThrow } from '#imports'
import { enumColor, getMdiIcon } from '@/utils'
const { formState, validateInfos, setAdditionalValidations, sqlUi, onDataTypeChange, onAlter } = useColumnCreateStoreOrThrow()
// cater existing v1 cases
const iconList = [
{
checked: 'mdi-check-bold',
unchecked: 'mdi-crop-square',
},
{
checked: 'mdi-check-circle-outline',
unchecked: 'mdi-checkbox-blank-circle-outline',
},
{
checked: 'mdi-star',
unchecked: 'mdi-star-outline',
},
{
checked: 'mdi-heart',
unchecked: 'mdi-heart-outline',
},
{
checked: 'mdi-moon-full',
unchecked: 'mdi-moon-new',
},
{
checked: 'mdi-thumb-up',
unchecked: 'mdi-thumb-up-outline',
},
{
checked: 'mdi-flag',
unchecked: 'mdi-flag-outline',
},
]
const advanced = ref(true)
const picked = ref(formState.value.meta.color || enumColor.light[0])
// set default value
formState.value.meta = {
icon: {
checked: 'mdi-check-bold',
unchecked: 'mdi-crop-square',
},
color: '#777',
...formState.value.meta,
}
</script>
<template>
<a-row>
<a-col :span="24">
<a-form-item label="Icon">
<a-select v-model:value="formState.meta.icon" size="small" class="w-52">
<!-- FIXME: antdv doesn't support object as value -->
<a-select-option v-for="(icon, i) of iconList" :key="i" :value="icon">
<component
:is="getMdiIcon(icon.checked)"
:style="{
color: formState.meta.color,
}"
/>
{{ ' ' }}
<component
:is="getMdiIcon(icon.unchecked)"
:style="{
color: formState.meta.color,
}"
/>
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-card class="w-full shadow-lg mt-2" body-style="padding: 0px">
<a-collapse v-model:activeKey="advanced" accordion ghost expand-icon-position="right">
<a-collapse-panel key="1" header="Advanced" class="">
<a-button class="!bg-primary text-white w-full" @click="formState.meta.color = picked.hex"> Pick Color </a-button>
<div class="px-7 py-3">
<Sketch v-model="picked" />
</div>
</a-collapse-panel>
</a-collapse>
</a-card>
</a-row>
</template>
<style scoped lang="scss">
.color-selector:hover {
@apply brightness-90;
}
.color-selector.selected {
@apply py-[5px] px-[10px] brightness-90;
}
</style>

1
packages/nc-gui-v2/components/smartsheet-column/EditOrAdd.vue

@ -91,6 +91,7 @@ watchEffect(() => {
<SmartsheetColumnCurrencyOptions v-if="formState.uidt === UITypes.Currency" />
<SmartsheetColumnDurationOptions v-if="formState.uidt === UITypes.Duration" />
<SmartsheetColumnRatingOptions v-if="formState.uidt === UITypes.Rating" />
<SmartsheetColumnCheckboxOptions v-if="formState.uidt === UITypes.Checkbox" />
<div>
<div

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 { computed, useColumn, useDebounceFn, useVModel } from '#imports'
import { ColumnInj, EditModeInj } from '~/context'
import { NavigateDir } from '~/lib'
interface Props {
column: ColumnType
@ -17,14 +18,15 @@ interface Emits {
const { column, ...props } = defineProps<Props>()
const emit = defineEmits(['update:modelValue', 'save'])
const emit = defineEmits(['update:modelValue', 'save', 'navigate', 'update:editEnabled'])
provide(ColumnInj, column)
provide(EditModeInj, toRef(props, 'editEnabled'))
provide(EditModeInj, useVModel(props, 'editEnabled', emit))
let changed = $ref(false)
const syncValue = useDebounceFn(function () {
changed = false
emit('save')
}, 1000)
@ -59,6 +61,7 @@ const vModel = computed({
syncValue()
} else if (!isManualSaved) {
emit('save')
changed = true
}
}
},
@ -87,10 +90,26 @@ const {
isPercent,
isPhoneNumber,
} = useColumn(column)
const syncAndNavigate = (dir: NavigateDir) => {
if (changed) {
emit('save')
changed = false
}
emit('navigate', dir)
}
</script>
<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" />
<CellCheckbox v-else-if="isBoolean" v-model="vModel" />
<CellAttachment v-else-if="isAttachment" v-model="vModel" />

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

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

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

@ -2,6 +2,7 @@
import type { ColumnType } from 'nocodb-sdk'
import { provide, useVirtualCell } from '#imports'
import { ColumnInj } from '~/context'
import { NavigateDir } from '~/lib'
interface Props {
column: ColumnType
@ -10,7 +11,7 @@ interface Props {
const { column, modelValue: value } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(['update:modelValue', 'navigate'])
provide(ColumnInj, column)
provide('value', value)
@ -19,196 +20,20 @@ const { isLookup, isBt, isRollup, isMm, isHm, isFormula, isCount } = useVirtualC
</script>
<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" />
<VirtualCellManyToMany v-else-if="isMm" />
<VirtualCellBelongsTo v-else-if="isBt" />
<VirtualCellRollup v-else-if="isRollup" />
<VirtualCellFormula v-else-if="isFormula" />
<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>
</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>
.nc-hint {
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)
watch(tabMeta, async (newTabMeta, oldTabMeta) => {
if (newTabMeta !== oldTabMeta && newTabMeta.id) await getMeta(newTabMeta.id)
if (newTabMeta !== oldTabMeta && newTabMeta?.id) await getMeta(newTabMeta.id)
})
</script>

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

@ -86,7 +86,7 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
// actions
const generateNewColumnMeta = () => {
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 = () => {

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) => {
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 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',
pt_BR = 'Português (Brasil)',
}
export enum NavigateDir {
NEXT,
PREV,
}

Loading…
Cancel
Save