Browse Source

feat(gui-v2): add formula cell

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/2716/head
Pranav C 2 years ago
parent
commit
283cbfd391
  1. 2
      packages/nc-gui-v2/components/smartsheet/VirtualCell.vue
  2. 36
      packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue
  3. 64
      packages/nc-gui-v2/components/virtual-cell/Formula.vue
  4. 93
      packages/nc-gui-v2/components/virtual-cell/FormulaCell.vue
  5. 8
      packages/nc-gui-v2/components/virtual-cell/Lookup.vue
  6. 73
      packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue
  7. 4
      packages/nc-gui-v2/composables/useGlobalState.ts
  8. 3
      packages/nc-gui-v2/composables/useProject.ts
  9. 5
      packages/nc-gui-v2/layouts/default.vue
  10. 16
      packages/nc-gui-v2/utils/dateTimeUtils.ts
  11. 18
      packages/nc-gui-v2/utils/urlUtils.ts

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

@ -18,7 +18,7 @@ const { isLookup, isBt, isRollup, isMm, isHm, isFormula } = useVirtualCell(colum
<VirtualCellManyToMany v-else-if="isMm" />
<VirtualCellBelongsTo v-else-if="isBt" />
<VirtualCellRollup v-else-if="isRollup" />
<VirtualCellRollup v-else-if="isRollup" />
<VirtualCellFormula v-else-if="isFormula" />
<!-- <v-lazy> -->
<!-- <has-many-cell

36
packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue

@ -1,16 +1,15 @@
<script setup lang="ts">
import type { ColumnType } from 'nocodb-sdk'
import ItemChip from './components/ItemChip.vue'
import useBelongsTo from '~/composables/useBelongsTo'
import { ColumnType } from "nocodb-sdk";
import useBelongsTo from "~/composables/useBelongsTo";
import ItemChip from "./components/ItemChip.vue";
const column = inject<ColumnType>('column')
const value = inject('value')
const column = inject<ColumnType>("column");
const value = inject("value");
const { parentMeta, loadParentMeta, primaryValueProp } = useBelongsTo(column as ColumnType);
await loadParentMeta();
const { parentMeta, loadParentMeta, primaryValueProp } = useBelongsTo(column as ColumnType)
await loadParentMeta()
// import ApiFactory from '@/components/project/spreadsheet/apis/apiFactory'
/*import { RelationTypes, UITypes, isSystemColumn } from 'nocodb-sdk'
/* import { RelationTypes, UITypes, isSystemColumn } from 'nocodb-sdk'
import ListItems from '~/components/project/spreadsheet/components/virtualCell/components/ListItems'
import ListChildItems from '~/components/project/spreadsheet/components/virtualCell/components/ListChildItems'
import ItemChip from '~/components/project/spreadsheet/components/virtualCell/components/ItemChip'
@ -306,23 +305,18 @@ export default {
}, 500)
},
},
}*/
} */
</script>
<template>
<div class="d-flex d-100 chips-wrapper" :class="{ active }">
<!-- <template v-if="!isForm">-->
<!-- <template v-if="!isForm"> -->
<div class="chips d-flex align-center img-container flex-grow-1 hm-items">
<template v-if="value || localState">
<ItemChip
:active="active"
:item="value"
:value="value[primaryValueProp]"
/>
<ItemChip :active="active" :item="value" :value="value[primaryValueProp]" />
<!-- :readonly="isLocked || (isPublic && !isForm)"
@edit="editParent"
@unlink="unlink"-->
@unlink="unlink" -->
</template>
</div>
<!-- <div
@ -333,8 +327,8 @@ export default {
<x-icon small :color="['primary', 'grey']" @click="showNewRecordModal">
{{ value ? 'mdi-arrow-expand' : 'mdi-plus' }}
</x-icon>
</div>-->
<!-- </template>-->
</div> -->
<!-- </template> -->
<!-- <ListItems
v-if="newRecordModal"
:key="parentId"
@ -416,7 +410,7 @@ export default {
"
@input="onParentSave"
/>
</v-dialog>-->
</v-dialog> -->
</div>
</template>

64
packages/nc-gui-v2/components/virtual-cell/Formula.vue

@ -0,0 +1,64 @@
<script lang="ts" setup>
import type { ColumnType } from 'nocodb-sdk'
import { computed } from 'vue'
import { handleTZ } from '~/utils/dateTimeUtils'
import { replaceUrlsWithLink } from '~/utils/urlUtils'
const column = inject<ColumnType>('column')
const value = inject('value')
const { isPg } = useProject()
const showEditFormulaWarning = ref(false)
const showEditFormulaWarningMessage = () => {
showEditFormulaWarning.value = true
setTimeout(() => {
showEditFormulaWarning.value = false
}, 3000)
}
const result = computed(() => {
if (isPg) {
return handleTZ(value)
}
return value
})
const urls = computed(() => {
return replaceUrlsWithLink(result.value)
})
</script>
<template>
<div>
<v-tooltip v-if="column && column.colOptions && column.colOptions.error" bottom color="error">
<template #activator="{ on }">
<span class="caption" v-on="on">ERR<span class="error--text">!</span></span>
</template>
<span class="font-weight-bold">{{ column.colOptions.error }}</span>
</v-tooltip>
<div class="formula-cell-wrapper" @dblclick="showEditFormulaWarningMessage">
<div v-if="urls" v-html="urls" />
<div v-else>
{{ result }}
</div>
<div v-if="showEditFormulaWarning" class="edit-warning">
<!-- TODO: i18n -->
Warning: Formula fields should be configured in the field menu dropdown.
</div>
</div>
</div>
</template>
<style scoped>
.formula-cell-wrapper {
padding: 10px;
}
.edit-warning {
text-align: left;
margin-top: 10px;
color: #e65100;
}
</style>

93
packages/nc-gui-v2/components/virtual-cell/FormulaCell.vue

@ -1,93 +0,0 @@
<script>
import dayjs from 'dayjs'
export default {
name: 'FormulaCell',
props: { column: Object, row: Object, client: String },
data: () => ({
showEditFormulaWarning: false,
}),
computed: {
result() {
if (this.client === 'pg') {
return this.handleTZ(this.row[this.column.title])
}
return this.row[this.column.title]
},
urls() {
if (!this.row[this.column.title]) {
return
}
const rawText = this.result.toString()
let found = false
const out = rawText.replace(/URI::\((.*?)\)/g, (_, url) => {
found = true
const a = document.createElement('a')
a.textContent = url
a.setAttribute('href', url)
a.setAttribute('target', '_blank')
return a.outerHTML
})
return found && out
},
},
methods: {
// handle date returned from PostgreSQL
handleTZ(val) {
if (!val) {
return
}
if (typeof val !== 'string') {
return val
}
return val.replace(
/((?:-?(?:[1-9][0-9]*)?[0-9]{4})-(?:1[0-2]|0[1-9])-(?:3[01]|0[1-9]|[12][0-9])T(?:2[0-3]|[01][0-9]):(?:[0-5][0-9]):(?:[0-5][0-9])(?:\.[0-9]+)?(?:Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9]))/g,
(i, v) => {
return dayjs(v).format('YYYY-MM-DD HH:mm')
},
)
},
showEditFormulaWarningMessage() {
this.showEditFormulaWarning = true
setTimeout(() => {
this.showEditFormulaWarning = false
}, 3000)
},
},
}
</script>
<template>
<div>
<v-tooltip v-if="column && column.colOptions && column.colOptions.error" bottom color="error">
<template #activator="{ on }">
<span class="caption" v-on="on">ERR<span class="error--text">!</span></span>
</template>
<span class="font-weight-bold">{{ column.colOptions.error }}</span>
</v-tooltip>
<div class="formula-cell-wrapper" @dblclick="showEditFormulaWarningMessage">
<div v-if="urls" v-html="urls" />
<div v-else>
{{ result }}
</div>
<div v-if="showEditFormulaWarning == true" class="edit-warning">
<!-- TODO: i18n -->
Warning: Formula fields should be configured in the field menu dropdown.
</div>
</div>
</div>
</template>
<style scoped>
.formula-cell-wrapper {
padding: 10px;
}
.edit-warning {
text-align: left;
margin-top: 10px;
color: #e65100;
}
</style>

8
packages/nc-gui-v2/components/virtual-cell/Lookup.vue

@ -1,5 +1,5 @@
<script>
/*import { RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk'
/* import { RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk'
import TableCell from '../Cell'
import ItemChip from '~/components/project/spreadsheet/components/virtualCell/components/ItemChip'
export default {
@ -85,12 +85,12 @@ export default {
this.lookupListModal = true
},
},
}*/
} */
</script>
<template>
<div class="d-flex flex-wrap wrapper">
<!-- <template v-if="lookupColumnMeta">
<!-- <template v-if="lookupColumnMeta">
<template v-if="isVirtualCol(lookupColumnMeta)">
<template
:is="virtualCell"
@ -148,7 +148,7 @@ export default {
</ItemChip>
</template>
</template>
</template>-->
</template> -->
</div>
</template>

73
packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue

@ -1,13 +1,13 @@
<script setup lang="ts">
import type { ColumnType } from "nocodb-sdk";
import ItemChip from "./components/ItemChip.vue";
import useManyToMany from "~/composables/useManyToMany";
import type { ColumnType } from 'nocodb-sdk'
import ItemChip from './components/ItemChip.vue'
import useManyToMany from '~/composables/useManyToMany'
const column = inject<ColumnType>("column");
const value = inject("value");
const column = inject<ColumnType>('column')
const value = inject('value')
const { childMeta, loadChildMeta, primaryValueProp } = useManyToMany(column as ColumnType);
await loadChildMeta();
const { childMeta, loadChildMeta, primaryValueProp } = useManyToMany(column as ColumnType)
await loadChildMeta()
/* import { RelationTypes, UITypes, isSystemColumn } from 'nocodb-sdk'
import DlgLabelSubmitCancel from '~/components/utils/DlgLabelSubmitCancel'
@ -395,43 +395,38 @@ export default {
<template>
<div class="d-flex d-100 chips-wrapper" :class="{ active }">
<!-- <template v-if="!isForm"> -->
<div class="chips d-flex align-center img-container flex-grow-1 hm-items flex-nowrap">
<template v-if="value || localState">
<ItemChip
v-for="(v, j) in value || localState"
:key="j"
:item="v"
:value="v[primaryValueProp]"
/>
<!-- <template v-if="!isForm"> -->
<div class="chips d-flex align-center img-container flex-grow-1 hm-items flex-nowrap">
<template v-if="value || localState">
<ItemChip v-for="(v, j) in value || localState" :key="j" :item="v" :value="v[primaryValueProp]" />
<!-- :active="active"
<!-- :active="active"
:readonly="isLocked || isPublic"
@edit="editChild"
@unlink="unlinkChild" -->
</template>
<span v-if="!isLocked && value && value.length === 10" class="caption pointer ml-1 grey--text" @click="showChildListModal"
>more...</span
>
</div>
<!-- <div -->
<!-- v-if="!isLocked" -->
<!-- class="actions align-center justify-center px-1 flex-shrink-1" -->
<!-- :class="{ 'd-none': !active, 'd-flex': active }" -->
<!-- > -->
<!-- <x-icon -->
<!-- v-if="_isUIAllowed('xcDatatableEditable') && (isForm || !isPublic)" -->
<!-- small -->
<!-- :color="['primary', 'grey']" -->
<!-- @click="showNewRecordModal" -->
<!-- > -->
<!-- mdi-plus -->
<!-- </x-icon> -->
<!-- <x-icon x-small :color="['primary', 'grey']" class="ml-2" @click="showChildListModal"> mdi-arrow-expand </x-icon> -->
<!-- </div> -->
<!-- </template>-->
</template>
<span v-if="!isLocked && value && value.length === 10" class="caption pointer ml-1 grey--text" @click="showChildListModal"
>more...</span
>
</div>
<!-- <div -->
<!-- v-if="!isLocked" -->
<!-- class="actions align-center justify-center px-1 flex-shrink-1" -->
<!-- :class="{ 'd-none': !active, 'd-flex': active }" -->
<!-- > -->
<!-- <x-icon -->
<!-- v-if="_isUIAllowed('xcDatatableEditable') && (isForm || !isPublic)" -->
<!-- small -->
<!-- :color="['primary', 'grey']" -->
<!-- @click="showNewRecordModal" -->
<!-- > -->
<!-- mdi-plus -->
<!-- </x-icon> -->
<!-- <x-icon x-small :color="['primary', 'grey']" class="ml-2" @click="showChildListModal"> mdi-arrow-expand </x-icon> -->
<!-- </div> -->
<!-- </template> -->
<!-- <ListItems
<!-- <ListItems
v-if="newRecordModal"
v-model="newRecordModal"
:hm="true"

4
packages/nc-gui-v2/composables/useGlobalState.ts

@ -11,7 +11,9 @@ export const useGlobalState = (): GlobalState => {
const preferredLanguages = $(usePreferredLanguages())
const darkMode = $(usePreferredDark())
const initialState: State = { token: null, user: null, lang: preferredLanguages[0] || 'en', darkMode }
const preferredLanguage = preferredLanguages[0]?.split('_')[0] || 'en'
const initialState: State = { token: null, user: null, lang: preferredLanguage, darkMode }
const storage = useStorage<State>(storageKey, initialState)

3
packages/nc-gui-v2/composables/useProject.ts

@ -20,6 +20,7 @@ export default () => {
}
const isMysql = computed(() => ['mysql', 'mysql2'].includes(project.value?.bases?.[0]?.type || ''))
const isPg = computed(() => project.value?.bases?.[0]?.type === 'pg')
return { project, tables, loadProject, loadTables, isMysql }
return { project, tables, loadProject, loadTables, isMysql, isPg }
}

5
packages/nc-gui-v2/layouts/default.vue

@ -3,10 +3,11 @@ import { useI18n } from 'vue-i18n'
import { useHead, useRoute } from '#imports'
const route = useRoute()
const { t } = useI18n()
const { te, t } = useI18n()
useHead({
title: `${t(route.meta.title as string)} | NocoDB`,
title: route.meta?.title && te(route.meta.title as string) ? `${t(route.meta.title as string)} | NocoDB` : 'NocoDB',
})
</script>

16
packages/nc-gui-v2/utils/dateTimeUtils.ts

@ -0,0 +1,16 @@
import dayjs from 'dayjs'
export const handleTZ = (val: any) => {
if (!val) {
return
}
if (typeof val !== 'string') {
return val
}
return val.replace(
/((?:-?(?:[1-9][0-9]*)?[0-9]{4})-(?:1[0-2]|0[1-9])-(?:3[01]|0[1-9]|[12][0-9])T(?:2[0-3]|[01][0-9]):(?:[0-5][0-9]):(?:[0-5][0-9])(?:\.[0-9]+)?(?:Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9]))/g,
(i, v) => {
return dayjs(v).format('YYYY-MM-DD HH:mm')
},
)
}

18
packages/nc-gui-v2/utils/urlUtils.ts

@ -0,0 +1,18 @@
export const replaceUrlsWithLink = (text: string): boolean | string => {
if (!text) {
return false
}
const rawText = text.toString()
let found = false
const out = rawText.replace(/URI::\((.*?)\)/g, (_, url) => {
found = true
const a = document.createElement('a')
a.textContent = url
a.setAttribute('href', url)
a.setAttribute('target', '_blank')
return a.outerHTML
})
return found && out
}
Loading…
Cancel
Save