Browse Source

refactor/gui-v2 added galllery view

pull/3021/head
Muhammed Mustafa 2 years ago
parent
commit
ebd8e55c98
  1. 1
      packages/nc-gui-v2/components.d.ts
  2. 6
      packages/nc-gui-v2/components/smartsheet-header/Cell.vue
  3. 6
      packages/nc-gui-v2/components/smartsheet-header/VirtualCell.vue
  4. 242
      packages/nc-gui-v2/components/smartsheet/Gallery.vue
  5. 12
      packages/nc-gui-v2/composables/useViewData.ts

1
packages/nc-gui-v2/components.d.ts vendored

@ -23,6 +23,7 @@ declare module '@vue/runtime-core' {
AEmpty: typeof import('ant-design-vue/es')['Empty'] AEmpty: typeof import('ant-design-vue/es')['Empty']
AForm: typeof import('ant-design-vue/es')['Form'] AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem'] AFormItem: typeof import('ant-design-vue/es')['FormItem']
AImage: typeof import('ant-design-vue/es')['Image']
AInput: typeof import('ant-design-vue/es')['Input'] AInput: typeof import('ant-design-vue/es')['Input']
AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] AInputPassword: typeof import('ant-design-vue/es')['InputPassword']

6
packages/nc-gui-v2/components/smartsheet-header/Cell.vue

@ -20,8 +20,10 @@ useProvideColumnCreateStore(meta as Ref<TableType>, column)
<SmartsheetHeaderCellIcon v-if="column" /> <SmartsheetHeaderCellIcon v-if="column" />
<span v-if="column" class="name" style="white-space: nowrap" :title="column.title">{{ column.title }}</span> <span v-if="column" class="name" style="white-space: nowrap" :title="column.title">{{ column.title }}</span>
<div class="flex-1" /> <template v-if="!hideMenu">
<SmartsheetHeaderMenu /> <div class="flex-1" />
<SmartsheetHeaderMenu />
</template>
</div> </div>
</template> </template>

6
packages/nc-gui-v2/components/smartsheet-header/VirtualCell.vue

@ -95,9 +95,11 @@ useProvideColumnCreateStore(meta as Ref<TableType>, column)
<!-- <span class="caption" v-html="tooltipMsg" /> --> <!-- <span class="caption" v-html="tooltipMsg" /> -->
<!-- </v-tooltip> --> <!-- </v-tooltip> -->
<v-spacer /> <template v-if="!hideMenu">
<v-spacer />
<SmartsheetHeaderMenu :virtual="true" /> <SmartsheetHeaderMenu :virtual="true" />
</template>
</div> </div>
</template> </template>

242
packages/nc-gui-v2/components/smartsheet/Gallery.vue

@ -1,7 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { isVirtualCol } from 'nocodb-sdk' import { isVirtualCol } from 'nocodb-sdk'
import { inject, provide, useViewData } from '#imports' import { inject, provide, useViewData } from '#imports'
import { ActiveViewInj, ChangePageInj, IsFormInj, IsGridInj, MetaInj, PaginationDataInj, ReadonlyInj } from '~/context' import { ActiveViewInj, ChangePageInj, FieldsInj, IsFormInj, IsGridInj, MetaInj, PaginationDataInj, ReadonlyInj } from '~/context'
import ImageIcon from '~icons/mdi/file-image-box'
const meta = inject(MetaInj) const meta = inject(MetaInj)
const view = inject(ActiveViewInj) const view = inject(ActiveViewInj)
@ -12,7 +13,7 @@ const isPublicView = false
const selected = reactive<{ row?: number | null; col?: number | null }>({}) const selected = reactive<{ row?: number | null; col?: number | null }>({})
const editEnabled = ref(false) const editEnabled = ref(false)
const { loadData, paginationData, formattedData: data, updateRowProperty, changePage } = useViewData(meta, view as any) const { loadData, paginationData, formattedData: data, loadGalleryData, galleryData, changePage } = useViewData(meta, view as any)
provide(IsFormInj, false) provide(IsFormInj, false)
provide(IsGridInj, false) provide(IsGridInj, false)
@ -20,11 +21,24 @@ provide(PaginationDataInj, paginationData)
provide(ChangePageInj, changePage) provide(ChangePageInj, changePage)
provide(ReadonlyInj, true) provide(ReadonlyInj, true)
const fields = inject(FieldsInj, ref([]))
const coverImageColumn = $(computed(() => fields.value.find((col) => col.id === galleryData.value?.fk_cover_image_col_id)))
const coverUrl = (record: any) => {
try {
return JSON.parse(record.row[coverImageColumn?.title])[0].url
} catch (e) {
return null
}
}
watch( watch(
[meta, view], [meta, view],
async () => { async () => {
if (meta?.value && view?.value) { if (meta?.value && view?.value) {
await loadData() await loadData()
await loadGalleryData()
} }
}, },
{ immediate: true }, { immediate: true },
@ -32,212 +46,36 @@ watch(
</script> </script>
<template> <template>
<v-container fluid class="nc-gallery-container"> <div class="grid grid-cols-4 gap-x-4 gap-y-4 pt-4 overflow-y-scroll h-full px-3">
<!-- <v-row class="align-stretch"> <div v-for="(record, i) in data" :key="i" class="flex flex-col">
<v-col v-for="({ row }, rowIndex) in data" :key="rowIndex" <a-card hoverable class="!rounded-lg">
md="4" <template #cover>
lg="3" <img v-if="record.row[coverImageColumn?.title]" class="h-52 rounded-t-lg" :src="coverUrl(record)" />
sm="6" <ImageIcon v-else class="w-full h-48 my-4 text-cool-gray-200" />
xs="12"> --> </template>
<template v-for="(col, colIndex) in fields" :key="colIndex">
<!-- <v-hover v-slot="{hover}"> --> <div class="flex flex-col space-y-1 px-4 mb-6 bg-gray-50 rounded-lg w-full">
<div class="flex flex-row w-full justify-start border-b-1 border-gray-100 py-2.5">
<div class="w-full text-gray-600">
<SmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" :hide-menu="true" />
<SmartsheetHeaderCell v-else :column="col" :hide-menu="true" />
</div>
</div>
<div class="flex flex-wrap gap-4 justify-center"> <div class="flex flex-row w-full pb-3 pt-2 pl-2 items-center justify-start">
<div v-for="({ row }, rowIndex) in data" :key="rowIndex" class="md:w-[300px] lg:w-[400px] xl:w-[500px]"> <div v-if="!record.row[col.title]" class="h-3 bg-gray-200 px-5 rounded-lg"></div>
<v-card class="h-100">
<!-- :elevation="hover ? 4 : 1"
:ripple="!isLocked"
@click="!isLocked && $emit('expandForm', {row,rowIndex,rowMeta})"
> -->
<!-- <v-carousel
v-if="attachmentColumn"
:continuous="false"
:cycle="true"
:show-arrows="false"
hide-delimiter-background
delimiter-icon="mdi-minus"
height="200"
>
<v-carousel-item
v-for="(cover, i) in getCovers(row)"
:key="i"
>
<v-img
height="200"
:src="cover.url"
:alt="cover.title"
/>
</v-carousel-item>
</v-carousel> -->
<!-- <v-card-title --> <SmartsheetVirtualCell v-if="isVirtualCol(col)" v-model="record.row[col.title]" :column="col" />
<!-- class="text-capitalize" -->
<!-- v-text="row[primaryValueColumn]" -->
<!-- /> -->
<v-card-text>
<v-container fluid>
<!-- <v-row class="">
<v-col v-for="col in meta.columns" :key="col.title"
class="col-12"> -->
<div v-for="col in meta.columns" :key="col.title" class="my-8">
<!-- <v-col v-for="col in fields" v-show="showFields[col.title]" :key="col.title" class="col-12 mt-1 mb-2"> -->
<!--
todo:header cell
-->
<label :for="`data-table-form-${col.title}`" class="body-2 text-capitalize caption grey--text">
<SmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" />
<SmartsheetHeaderCell v-else :column="col" />
<!-- <virtual-header-cell
v-if="isVirtualCol(col)"
:column="col"
:nodes="nodes"
:is-form="true"
:meta="meta"
/>
<header-cell
v-else
:is-form="true"
:value="col.title"
:column="col"
/> -->
</label>
<div class="mt-2">
<SmartsheetVirtualCell v-if="isVirtualCol(col)" v-model="row[col.title]" :edit-enabled="false" :column="col" />
<SmartsheetCell <SmartsheetCell v-else v-model="record.row[col.title]" :column="col" :edit-enabled="false" />
v-else </div>
v-model="row[col.title]" </div>
:edit-enabled="false" </template>
:column="col" </a-card>
@update:model-value="updateRowProperty(row, col.title)"
/>
</div>
<!-- <virtual-cell
v-if="isVirtualCol(col)"
ref="virtual"
:column="col"
:row="row"
:nodes="nodes"
:meta="meta"
/>
<table-cell
v-else
:value="row[col.title]"
:column="col"
:sql-ui="sqlUi"
:is-locked="isLocked"
class="xc-input body-2"
:meta="meta"
/> -->
<!-- </v-col> -->
<!-- </v-row> -->
</div>
</v-container>
</v-card-text>
</v-card>
</div>
</div> </div>
</div>
<!-- </v-hover> -->
<!-- </v-col>
</v-row> -->
</v-container>
</template> </template>
<!--
<script>
import { isVirtualCol } from "nocodb-sdk";
import VirtualHeaderCell from "../components/VirtualHeaderCell";
import HeaderCell from "../components/HeaderCell";
import VirtualCell from "../components/VirtualCell";
import TableCell from "../components/Cell";
export default {
name: "GalleryView",
components: {
TableCell,
VirtualCell,
HeaderCell,
VirtualHeaderCell
},
props: [
"nodes",
"table",
"showFields",
"availableColumns",
"meta",
"data",
"primaryValueColumn",
"showSystemFields",
"sqlUi",
"coverImageField",
"viewId",
"isLocked"
],
data() {
return {
galleryView: {}
};
},
computed: {
attachmentColumn() {
return this.coverImageField && this.meta && this.meta.columns && this.meta.columns.find(c => c.id === this.coverImageField);
},
fields() {
if (this.availableColumns) {
return this.availableColumns;
}
const hideCols = ["created_at", "updated_at"];
if (this.showSystemFields) {
return this.meta.columns || [];
} else {
return this.meta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.title) &&
!((this.meta.v || []).some(v => v.bt && v.bt.title === c.title))
) || [];
}
}
},
watch: {
async coverImageField(v) {
if (this.galleryView && v !== this.galleryView.fk_cover_image_col_id) {
(await this.$api.dbView.galleryUpdate(this.viewId, {
...this.galleryView,
fk_cover_image_col_id: v
}));
}
}
},
created() {
this.loadView();
},
methods: {
isVirtualCol,
async loadView() {
this.galleryView = (await this.$api.dbView.galleryRead(this.viewId));
this.$emit("update:coverImageField", this.galleryView.fk_cover_image_col_id);
},
getCovers(row) {
if (this.attachmentColumn &&
row[this.attachmentColumn.title] && row[this.attachmentColumn.title][0] &&
row[this.attachmentColumn.title]) {
try {
return JSON.parse(row[this.attachmentColumn.title]);
} catch (e) {
}
}
return [{ url: "https://via.placeholder.com/700?text=No%20image%20found" }];
}
}
};
</script>
<style scoped>
</style>
-->
<style scoped> <style scoped>
.nc-gallery-container { .nc-gallery-container {
height: calc(100vh - 160px); height: calc(100vh - 160px);

12
packages/nc-gui-v2/composables/useViewData.ts

@ -1,4 +1,4 @@
import type { Api, PaginatedType, TableType, ViewType } from 'nocodb-sdk' import type { Api, GalleryType, PaginatedType, TableType, ViewType } from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue' import type { ComputedRef, Ref } from 'vue'
import { notification } from 'ant-design-vue' import { notification } from 'ant-design-vue'
import { useNuxtApp } from '#app' import { useNuxtApp } from '#app'
@ -26,6 +26,7 @@ export function useViewData(
) { ) {
const formattedData = ref<Row[]>([]) const formattedData = ref<Row[]>([])
const paginationData = ref<PaginatedType>({ page: 1, pageSize: 25 }) const paginationData = ref<PaginatedType>({ page: 1, pageSize: 25 })
const galleryData = ref<GalleryType | undefined>(undefined)
const { project } = useProject() const { project } = useProject()
const { $api } = useNuxtApp() const { $api } = useNuxtApp()
@ -58,6 +59,13 @@ export function useViewData(
formattedData.value = formatData(response.list) formattedData.value = formatData(response.list)
paginationData.value = response.pageInfo paginationData.value = response.pageInfo
} }
const loadGalleryData = async () => {
if (!viewMeta?.value?.id) return
galleryData.value = await $api.dbView.galleryRead(viewMeta.value.id)
}
const insertRow = async (row: Record<string, any>, rowIndex = formattedData.value?.length) => { const insertRow = async (row: Record<string, any>, rowIndex = formattedData.value?.length) => {
try { try {
const insertObj = meta?.value?.columns?.reduce((o: any, col) => { const insertObj = meta?.value?.columns?.reduce((o: any, col) => {
@ -248,5 +256,7 @@ export function useViewData(
updateOrSaveRow, updateOrSaveRow,
selectedAllRecords, selectedAllRecords,
syncCount, syncCount,
galleryData,
loadGalleryData,
} }
} }

Loading…
Cancel
Save