Browse Source

wip(gui-v2): view create

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/2716/head
Pranav C 2 years ago
parent
commit
8b9d1cd37c
  1. 10
      packages/nc-gui-v2/components/cell/Duration.vue
  2. 97
      packages/nc-gui-v2/components/dlg/VueCreate.vue
  3. 1
      packages/nc-gui-v2/components/index.ts
  4. 119
      packages/nc-gui-v2/components/smartsheet/Sidebar.vue
  5. 41
      packages/nc-gui-v2/composables/useViewCreate.ts
  6. 72
      packages/nc-gui-v2/utils/durationHelper.ts

10
packages/nc-gui-v2/components/cell/Duration.vue

@ -1,17 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, inject } from '#imports' import { computed, inject, ref } from '#imports'
import { ColumnInj } from '~/components' import { ColumnInj } from '~/components'
import { convertDurationToSeconds, convertMS2Duration, durationOptions } from '~/utils/durationHelper' import { convertDurationToSeconds, convertMS2Duration, durationOptions } from '~/utils/durationHelper'
const { modelValue } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const column = inject(ColumnInj) const column = inject(ColumnInj)
interface Props { interface Props {
modelValue: number | string modelValue: number | string
} }
const { modelValue } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const showWarningMessage = ref(false) const showWarningMessage = ref(false)
const durationInMS = ref(0) const durationInMS = ref(0)
const isEdited = ref(false) const isEdited = ref(false)

97
packages/nc-gui-v2/components/dlg/VueCreate.vue

@ -0,0 +1,97 @@
<script setup lang="ts">
import { ViewTypes } from "nocodb-sdk";
import useViewCreate from "~/composables/useViewCreate";
const { modelValue, type } = defineProps<{ type: ViewTypes, modelValue: boolean }>();
const emit = defineEmits(["update:modelValue"]);
const dialogShow = computed({
get() {
return modelValue;
},
set(v) {
emit("update:modelValue", v);
}
});
const { view, createView, generateUniqueTitle } = useViewCreate(async () => {
});
/*import inflection from 'inflection'
export default {
name: 'DlgViewCreate',
props: ['value'],
data() {
return {
view: {
name: '',
},
}
},
computed: {
dialogShow: {
get() {
return this.value
},
set(v) {
this.$emit('input', v)
},
},
projectPrefix() {
return this.$store.getters['project/GtrProjectPrefix']
},
},
watch: {
'view.alias': function (v) {
this.$set(this.view, 'name', `${this.projectPrefix || ''}${inflection.underscore(v)}`)
},
},
mounted() {
setTimeout(() => {
this.$refs.input.$el.querySelector('input').focus()
}, 100)
},
}*/
</script>
<template>
<v-dialog v-model="dialogShow" max-width="500">
<v-card class="elevation-20">
<v-card-title class="grey darken-2 subheading" style="height: 30px" />
<v-card-text class="pt-4 pl-4">
<p class="headline">
{{ $t('general.create') }} <span class="text-capitalize">{{ typeAlias }}</span> {{ $t('objects.view') }}
</p>
<v-form ref="form" v-model="valid" @submit.prevent="createView">
<!-- label="View Name" -->
<v-text-field
ref="name"
v-model="view.title"
:label="$t('labels.viewName')"
:rules="[
v => !!v || 'View name required',
v => viewsList.every(v1 => (v1.alias || v1.title) !== v) || 'View name should be unique',
]"
autofocus
/>
</v-form>
</v-card-text>
<v-card-actions class="pa-4">
<v-spacer />
<v-btn class="" small @click="$emit('input', false)">
{{ $t('general.cancel') }}
</v-btn>
<v-btn small :loading="loading" class="primary" :disabled="!valid" @click="createView">
{{ $t('general.submit') }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<style scoped></style>

1
packages/nc-gui-v2/components/index.ts

@ -18,3 +18,4 @@ export const ActiveViewInj: InjectionKey<Ref<(GridType | GalleryType | FormType
export const ReadonlyInj: InjectionKey<any> = Symbol('readonly-injection') export const ReadonlyInj: InjectionKey<any> = Symbol('readonly-injection')
export const ReloadViewDataHookInj: InjectionKey<EventHook<void>> = Symbol('reload-view-data-injection') export const ReloadViewDataHookInj: InjectionKey<EventHook<void>> = Symbol('reload-view-data-injection')
export const FieldsInj: InjectionKey<Ref<any[]>> = Symbol('fields-injection') export const FieldsInj: InjectionKey<Ref<any[]>> = Symbol('fields-injection')
export const ViewListInj: InjectionKey<Ref<any[]>> = Symbol('view-list-injection')

119
packages/nc-gui-v2/components/smartsheet/Sidebar.vue

@ -1,15 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { ViewTypes } from 'nocodb-sdk'
import type { TableType } from 'nocodb-sdk' import type { TableType } from 'nocodb-sdk'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { inject, onMounted, ref } from '#imports' import { inject, ref } from '#imports'
import { ActiveViewInj, MetaInj } from '~/components' import { ActiveViewInj, MetaInj } from '~/components'
import useViews from '~/composables/useViews' import useViews from '~/composables/useViews'
import { viewIcons } from '~/utils/viewUtils' import { viewIcons } from '~/utils/viewUtils'
import MdiPlusIcon from '~icons/mdi/plus'
const meta = inject(MetaInj) const meta = inject(MetaInj)
const activeView = inject(ActiveViewInj) const activeView = inject(ActiveViewInj)
const { views, loadViews } = useViews(meta as Ref<TableType>) const { views, loadViews } = useViews(meta as Ref<TableType>)
provide(ViewListInj, views)
const _isUIAllowed = (view: string) => {} const _isUIAllowed = (view: string) => {}
loadViews().then(() => { loadViews().then(() => {
@ -17,6 +22,15 @@ loadViews().then(() => {
}) })
const toggleDrawer = ref(false) const toggleDrawer = ref(false)
// todo: identify based on meta
const isView = ref(false)
const viewCreateType = ref<ViewTypes>()
const viewCreateDlg = ref<boolean>()
const openCreateViewDlg = (type: ViewTypes) => {
viewCreateDlg.value = true
viewCreateType.value = type
}
</script> </script>
<template> <template>
@ -144,8 +158,111 @@ const toggleDrawer = ref(false)
<!-- </draggable> --> <!-- </draggable> -->
<!-- </v-list-group> --> <!-- </v-list-group> -->
</v-list> </v-list>
<v-divider class="advance-menu-divider" />
<v-list dense>
<v-list-item dense>
<!-- Create a View -->
<span class="body-2 font-weight-medium" @dblclick="enableDummyFeat = true">
{{ $t('activity.createView') }}
</span>
<v-tooltip top>
<template #activator="{ on }">
<!-- <x-icon -->
<!-- color="pink textColor" -->
<!-- icon-class="ml-2" -->
<!-- small -->
<!-- v-on="on" -->
<!-- @mouseenter="overShieldIcon = true" -->
<!-- @mouseleave="overShieldIcon = false" -->
<!-- > -->
<!-- mdi-shield-lock-outline -->
<!-- </x-icon> -->
</template>
<!-- Only visible to Creator -->
<span class="caption">
{{ $t('msg.info.onlyCreator') }}
</span>
</v-tooltip>
</v-list-item>
<v-tooltip bottom>
<template #activator="{ on }">
<v-list-item dense class="body-2 nc-create-grid-view" v-on="on" @click="openCreateViewDlg(viewTypes.GRID)">
<!-- <v-list-item-icon class="mr-n1"> -->
<component :is="viewIcons[ViewTypes.GRID].icon" :class="`text-${viewIcons[ViewTypes.GRID].color} mr-1`" />
<!-- </v-list-item-icon> -->
<v-list-item-title>
<span class="font-weight-regular">
<!-- Grid -->
{{ $t('objects.viewType.grid') }}
</span>
</v-list-item-title>
<v-spacer />
<MdiPlusIcon class="mr-1" />
<!-- <v-icon class="mr-1" small> mdi-plus</v-icon> -->
</v-list-item>
</template>
<!-- Add Grid View -->
{{ $t('msg.info.addView.grid') }}
</v-tooltip>
<v-tooltip bottom>
<template #activator="{ on }">
<v-list-item dense class="body-2 nc-create-gallery-view" v-on="on" @click="openCreateViewDlg(viewTypes.GALLERY)">
<!-- <v-list-item-icon class="mr-n1"> -->
<component :is="viewIcons[ViewTypes.GALLERY].icon" :class="`text-${viewIcons[ViewTypes.GALLERY].color} mr-1`" />
<!-- </v-list-item-icon> -->
<v-list-item-title>
<span class="font-weight-regular">
<!-- Gallery -->
{{ $t('objects.viewType.gallery') }}
</span>
</v-list-item-title>
<v-spacer />
<MdiPlusIcon class="mr-1" />
<!-- <v-icon class="mr-1" small> mdi-plus</v-icon> -->
</v-list-item>
</template>
<!-- Add Gallery View -->
{{ $t('msg.info.addView.gallery') }}
</v-tooltip>
<v-tooltip bottom>
<template #activator="{ on }">
<v-list-item
v-if="!isView"
dense
class="body-2 nc-create-form-view"
v-on="on"
@click="openCreateViewDlg(viewTypes.FORM)"
>
<!-- <v-list-item-icon class="mr-n1"> -->
<component :is="viewIcons[ViewTypes.FORM].icon" :class="`text-${viewIcons[ViewTypes.FORM].color} mr-1`" />
<!-- </v-list-item-icon> -->
<v-list-item-title>
<span class="font-weight-regular">
<!-- Form -->
{{ $t('objects.viewType.form') }}
</span>
</v-list-item-title>
<v-spacer />
<MdiPlusIcon class="mr-1" />
<!-- <v-icon class="mr-1" small> mdi-plus</v-icon> -->
</v-list-item>
</template>
<!-- Add Form View -->
{{ $t('msg.info.addView.form') }}
</v-tooltip>
</v-list>
</div> </div>
</div> </div>
<DlgViewCreate v-model="viewCreateDlg" :type="viewCreateType" />
</div> </div>
</template> </template>

41
packages/nc-gui-v2/composables/useViewCreate.ts

@ -0,0 +1,41 @@
import type { ViewTypes } from 'nocodb-sdk'
import { UITypes } from 'nocodb-sdk'
import { useNuxtApp } from '#app'
export default (onViewCreate?: (viewMeta: any) => void) => {
const view = reactive<{ title: string; type?: ViewTypes }>({
title: '',
})
const { $api } = useNuxtApp()
const createView = async () => {
if (!sqlUi?.value) return
const columns = sqlUi?.value?.getNewTableColumns().filter((col) => {
if (col.column_name === 'id' && table.columns.includes('id_ag')) {
Object.assign(col, sqlUi?.value?.getDataTypeForUiType({ uidt: UITypes.ID }, 'AG'))
col.dtxp = sqlUi?.value?.getDefaultLengthForDatatype(col.dt)
col.dtxs = sqlUi?.value?.getDefaultScaleForDatatype(col.dt)
return true
}
return table.columns.includes(col.column_name)
})
const tableMeta = await $api.dbTable.create(project?.value?.id as string, {
...table,
columns,
})
onViewCreate?.(tableMeta)
}
const generateUniqueTitle = () => {
// let c = 1
// while (tables?.value?.some((t) => t.title === `Sheet${c}`)) {
// c++
// }
// table.title = `Sheet${c}`
}
return { view, createView, generateUniqueTitle }
}

72
packages/nc-gui-v2/utils/durationHelper.ts

@ -3,28 +3,32 @@ export const durationOptions = [
id: 0, id: 0,
title: 'h:mm', title: 'h:mm',
example: '(e.g. 1:23)', example: '(e.g. 1:23)',
regex: /(\d+)(?::(\d+))?/ regex: /(\d+)(?::(\d+))?/,
}, { },
{
id: 1, id: 1,
title: 'h:mm:ss', title: 'h:mm:ss',
example: '(e.g. 3:45, 1:23:40)', example: '(e.g. 3:45, 1:23:40)',
regex: /(\d+)?(?::(\d+))?(?::(\d+))?/ regex: /(\d+)?(?::(\d+))?(?::(\d+))?/,
}, { },
{
id: 2, id: 2,
title: 'h:mm:ss.s', title: 'h:mm:ss.s',
example: '(e.g. 3:34.6, 1:23:40.0)', example: '(e.g. 3:34.6, 1:23:40.0)',
regex: /(\d+)?(?::(\d+))?(?::(\d+))?(?:.(\d{0,4})?)?/ regex: /(\d+)?(?::(\d+))?(?::(\d+))?(?:.(\d{0,4})?)?/,
}, { },
{
id: 3, id: 3,
title: 'h:mm:ss.ss', title: 'h:mm:ss.ss',
example: '(e.g. 3.45.67, 1:23:40.00)', example: '(e.g. 3.45.67, 1:23:40.00)',
regex: /(\d+)?(?::(\d+))?(?::(\d+))?(?:.(\d{0,4})?)?/ regex: /(\d+)?(?::(\d+))?(?::(\d+))?(?:.(\d{0,4})?)?/,
}, { },
{
id: 4, id: 4,
title: 'h:mm:ss.sss', title: 'h:mm:ss.sss',
example: '(e.g. 3.45.678, 1:23:40.000)', example: '(e.g. 3.45.678, 1:23:40.000)',
regex: /(\d+)?(?::(\d+))?(?::(\d+))?(?:.(\d{0,4})?)?/ regex: /(\d+)?(?::(\d+))?(?::(\d+))?(?:.(\d{0,4})?)?/,
} },
] ]
// pad zero // pad zero
@ -35,18 +39,20 @@ export const durationOptions = [
// e.g. 1 -> 001 // e.g. 1 -> 001
// e.g. 10 -> 010 // e.g. 10 -> 010
const padZero = (val: number, isSSS = false) => { const padZero = (val: number, isSSS = false) => {
return (val + '').padStart(isSSS ? 3 : 2, '0') return `${val}`.padStart(isSSS ? 3 : 2, '0')
} }
export const convertMS2Duration = (val: any, durationType: number) => { export const convertMS2Duration = (val: any, durationType: number) => {
if (val === "" || val === null || val === undefined) { return val } if (val === '' || val === null || val === undefined) {
return val
}
// 600.000 s --> 10:00 (10 mins) // 600.000 s --> 10:00 (10 mins)
const milliseconds = Math.round((val % 1) * 1000) const milliseconds = Math.round((val % 1) * 1000)
const centiseconds = Math.round(milliseconds / 10) const centiseconds = Math.round(milliseconds / 10)
const deciseconds = Math.round(centiseconds / 10) const deciseconds = Math.round(centiseconds / 10)
const hours = Math.floor(parseInt(val, 10) / (60 * 60)) const hours = Math.floor(parseInt(val, 10) / (60 * 60))
const minutes = Math.floor((parseInt(val, 10) - (hours * 60 * 60)) / 60) const minutes = Math.floor((parseInt(val, 10) - hours * 60 * 60) / 60)
const seconds = parseInt(val, 10) - (hours * 60 * 60) - (minutes * 60) const seconds = parseInt(val, 10) - hours * 60 * 60 - minutes * 60
if (durationType === 0) { if (durationType === 0) {
// h:mm // h:mm
@ -71,7 +77,7 @@ export const durationOptions = [
// 10:00 (10 mins) -> 600.000 s // 10:00 (10 mins) -> 600.000 s
const res = { const res = {
_sec: 0, _sec: 0,
_isValid: true _isValid: true,
} }
const durationRegex = durationOptions[durationType].regex const durationRegex = durationOptions[durationType].regex
if (durationRegex.test(val)) { if (durationRegex.test(val)) {
@ -88,7 +94,7 @@ export const durationOptions = [
// consider it as minutes // consider it as minutes
// e.g. 360 -> 06:00 // e.g. 360 -> 06:00
h = Math.floor(val / 60) h = Math.floor(val / 60)
mm = Math.floor((val - ((h * 3600)) / 60)) mm = Math.floor(val - (h * 3600) / 60)
ss = 0 ss = 0
} else { } else {
// consider it as seconds // consider it as seconds
@ -118,49 +124,46 @@ export const durationOptions = [
} else if (durationType === 2) { } else if (durationType === 2) {
// h:mm:ss.s (deciseconds) // h:mm:ss.s (deciseconds)
const ds = groups[4] || 0 const ds = groups[4] || 0
const len = Math.log(ds) * Math.LOG10E + 1 | 0 const len = (Math.log(ds) * Math.LOG10E + 1) | 0
const ms = ( const ms =
// e.g. len = 4: 1234 -> 1, 1456 -> 1 // e.g. len = 4: 1234 -> 1, 1456 -> 1
// e.g. len = 3: 123 -> 1, 191 -> 2 // e.g. len = 3: 123 -> 1, 191 -> 2
// e.g. len = 2: 12 -> 1 , 16 -> 2 // e.g. len = 2: 12 -> 1 , 16 -> 2
len === 4 (len === 4
? Math.round(ds / 1000) ? Math.round(ds / 1000)
: len === 3 : len === 3
? Math.round(ds / 100) ? Math.round(ds / 100)
: len === 2 : len === 2
? Math.round(ds / 10) ? Math.round(ds / 10)
// take whatever it is : // take whatever it is
: ds ds) * 100
) * 100
res._sec = h * 3600 + mm * 60 + ss * 1 + ms / 1000 res._sec = h * 3600 + mm * 60 + ss * 1 + ms / 1000
} else if (durationType === 3) { } else if (durationType === 3) {
// h:mm:ss.ss (centiseconds) // h:mm:ss.ss (centiseconds)
const cs = groups[4] || 0 const cs = groups[4] || 0
const len = Math.log(cs) * Math.LOG10E + 1 | 0 const len = (Math.log(cs) * Math.LOG10E + 1) | 0
const ms = ( const ms =
// e.g. len = 4: 1234 -> 12, 1285 -> 13 // e.g. len = 4: 1234 -> 12, 1285 -> 13
// e.g. len = 3: 123 -> 12, 128 -> 13 // e.g. len = 3: 123 -> 12, 128 -> 13
// check the third digit // check the third digit
len === 4 (len === 4
? Math.round(cs / 100) ? Math.round(cs / 100)
: len === 3 : len === 3
? Math.round(cs / 10) ? Math.round(cs / 10)
// take whatever it is : // take whatever it is
: cs cs) * 10
) * 10
res._sec = h * 3600 + mm * 60 + ss * 1 + ms / 1000 res._sec = h * 3600 + mm * 60 + ss * 1 + ms / 1000
} else if (durationType === 4) { } else if (durationType === 4) {
// h:mm:ss.sss (milliseconds) // h:mm:ss.sss (milliseconds)
let ms = groups[4] || 0 let ms = groups[4] || 0
const len = Math.log(ms) * Math.LOG10E + 1 | 0 const len = (Math.log(ms) * Math.LOG10E + 1) | 0
ms = ( ms =
// e.g. 1235 -> 124 // e.g. 1235 -> 124
// e.g. 1234 -> 123 // e.g. 1234 -> 123
len === 4 (len === 4
? Math.round(ms / 10) ? Math.round(ms / 10)
// take whatever it is : // take whatever it is
: ms ms) * 1
) * 1
res._sec = h * 3600 + mm * 60 + ss * 1 + ms / 1000 res._sec = h * 3600 + mm * 60 + ss * 1 + ms / 1000
} }
} else { } else {
@ -190,4 +193,3 @@ export const durationOptions = [
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
Loading…
Cancel
Save