Browse Source

Merge pull request #3172 from nocodb/fix/3156-bulk

fix(gui-v2): minor improvements
pull/3179/head
աɨռɢӄաօռɢ 2 years ago committed by GitHub
parent
commit
9721aa72ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      packages/nc-gui-v2/components.d.ts
  2. 3
      packages/nc-gui-v2/components/dashboard/TreeView.vue
  3. 6
      packages/nc-gui-v2/components/smartsheet-header/Cell.vue
  4. 4
      packages/nc-gui-v2/components/smartsheet-header/Menu.vue
  5. 7
      packages/nc-gui-v2/components/smartsheet-header/VirtualCell.vue
  6. 55
      packages/nc-gui-v2/components/smartsheet-header/VirtualCellIcon.vue
  7. 5
      packages/nc-gui-v2/components/smartsheet/Cell.vue
  8. 15
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  9. 23
      packages/nc-gui-v2/components/smartsheet/expanded-form/Comments.vue
  10. 6
      packages/nc-gui-v2/components/smartsheet/expanded-form/index.vue
  11. 2
      packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue
  12. 50
      packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue

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

@ -106,6 +106,7 @@ declare module '@vue/runtime-core' {
MdiCloseCircle: typeof import('~icons/mdi/close-circle')['default']
MdiCloseThick: typeof import('~icons/mdi/close-thick')['default']
MdiCodeJson: typeof import('~icons/mdi/code-json')['default']
MdiCog: typeof import('~icons/mdi/cog')['default']
MdiContentCopy: typeof import('~icons/mdi/content-copy')['default']
MdiContentSave: typeof import('~icons/mdi/content-save')['default']
MdiDatabaseOutline: typeof import('~icons/mdi/database-outline')['default']
@ -156,6 +157,7 @@ declare module '@vue/runtime-core' {
MdiRocketLaunchOutline: typeof import('~icons/mdi/rocket-launch-outline')['default']
MdiScriptTextOutline: typeof import('~icons/mdi/script-text-outline')['default']
MdiSearch: typeof import('~icons/mdi/search')['default']
MdiShieldLockOutline: typeof import('~icons/mdi/shield-lock-outline')['default']
MdiSlack: typeof import('~icons/mdi/slack')['default']
MdiStar: typeof import('~icons/mdi/star')['default']
MdiStarOutline: typeof import('~icons/mdi/star-outline')['default']

3
packages/nc-gui-v2/components/dashboard/TreeView.vue

@ -150,7 +150,7 @@ const activeTable = computed(() => {
<a-dropdown :trigger="['contextmenu']">
<div
class="p-2 flex-1 overflow-y-auto flex flex-column scrollbar-thin-dull"
class="pt-2 pl-2 pb-2 flex-1 overflow-y-auto flex flex-column scrollbar-thin-dull"
:class="{ 'mb-[20px]': isSharedBase }"
style="direction: rtl"
>
@ -334,6 +334,7 @@ const activeTable = computed(() => {
.nc-tree-item.active {
@apply !text-primary font-weight-bold after:(!opacity-20);
@apply border-r-3 border-indigo-500;
svg {
@apply !text-primary;

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

@ -24,7 +24,7 @@ function onVisibleChange() {
</script>
<template>
<div class="flex items-center w-full text-xs text-normal">
<div class="flex items-center w-full text-xs text-normal text-gray-500 font-weight-medium" :class="{ 'h-full': 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.rqd && !column.cdf) || required" class="text-red-500">&nbsp;*</span>
@ -62,4 +62,8 @@ function onVisibleChange() {
overflow: hidden;
text-overflow: ellipsis;
}
:deep(.ant-dropdown-trigger) {
@apply h-full;
}
</style>

4
packages/nc-gui-v2/components/smartsheet-header/Menu.vue

@ -53,8 +53,8 @@ const setAsPrimaryValue = async () => {
</script>
<template>
<a-dropdown placement="bottomRight" :trigger="['hover']">
<MdiMenuDownIcon class="text-grey nc-ui-dt-dropdown" />
<a-dropdown placement="bottomRight" :trigger="['click']">
<MdiMenuDownIcon class="h-full text-grey nc-ui-dt-dropdown cursor-pointer outline-0" />
<template #overlay>
<a-menu class="shadow bg-white">
<a-menu-item @click="emit('edit')">

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

@ -101,7 +101,7 @@ function onVisibleChange() {
</script>
<template>
<div class="flex items-center w-full text-xs text-normal">
<div class="flex items-center w-full text-xs text-gray-500 font-weight-medium" :class="{ 'h-full': column }">
<SmartsheetHeaderVirtualCellIcon v-if="column" />
<a-tooltip placement="bottom">
@ -114,6 +114,7 @@ function onVisibleChange() {
<span v-if="column.rqd || required" class="text-red-500">&nbsp;*</span>
<template v-if="!hideMenu">
<div class="flex-1" />
<SmartsheetHeaderMenu v-if="!isForm && isUIAllowed('edit-column')" :virtual="true" @edit="editColumnDropdown = true" />
</template>
@ -145,4 +146,8 @@ function onVisibleChange() {
overflow: hidden;
text-overflow: ellipsis;
}
:deep(.ant-dropdown-trigger) {
@apply h-full;
}
</style>

55
packages/nc-gui-v2/components/smartsheet-header/VirtualCellIcon.vue

@ -1,6 +1,7 @@
<script setup lang="ts">
import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk'
import type { ColumnType, LinkToAnotherRecordType, LookupType } from 'nocodb-sdk'
import { RelationTypes, UITypes } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { toRef } from 'vue'
import { ColumnInj } from '~/context'
import GenericIcon from '~icons/mdi/square-rounded'
@ -16,35 +17,65 @@ import TableColumnPlusBefore from '~icons/mdi/table-column-plus-before'
const props = defineProps<{ columnMeta?: ColumnType }>()
const columnMeta = toRef(props, 'columnMeta')
const column = inject(ColumnInj, ref(columnMeta))
const column = inject(ColumnInj, ref(columnMeta)) as Ref<ColumnType & { colOptions: LookupType }>
let relationColumn: ColumnType & { colOptions: LookupType }
if (column) {
const { isLookup, isBt, isRollup, isMm, isHm } = useVirtualCell(column as Ref<ColumnType>)
if (isLookup || isBt || isRollup || isMm || isHm) {
const meta = inject(MetaInj)
relationColumn = meta?.value.columns?.find((c) => c.id === column.value?.colOptions?.fk_relation_column_id) as ColumnType & {
colOptions: LinkToAnotherRecordType
}
}
}
const icon = computed(() => {
switch (column?.value?.uidt) {
case UITypes.LinkToAnotherRecord:
switch ((column?.value?.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:
return MMIcon
return { icon: MMIcon, color: 'text-pink-500' }
case RelationTypes.HAS_MANY:
return HMIcon
return { icon: HMIcon, color: 'text-yellow-500' }
case RelationTypes.BELONGS_TO:
return BTIcon
return { icon: BTIcon, color: 'text-sky-500' }
}
break
case UITypes.SpecificDBType:
return SpecificDBTypeIcon
return { icon: SpecificDBTypeIcon, color: 'text-grey' }
case UITypes.Formula:
return FormulaIcon
return { icon: FormulaIcon, color: 'text-grey' }
case UITypes.Lookup:
return TableColumnPlusBefore
switch ((relationColumn?.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:
return { icon: TableColumnPlusBefore, color: 'text-pink-500' }
case RelationTypes.HAS_MANY:
return { icon: TableColumnPlusBefore, color: 'text-yellow-500' }
case RelationTypes.BELONGS_TO:
return { icon: TableColumnPlusBefore, color: 'text-sky-500' }
}
return { icon: TableColumnPlusBefore, color: 'text-grey' }
case UITypes.Rollup:
return RollupIcon
switch ((relationColumn?.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:
return { icon: RollupIcon, color: 'text-pink-500' }
case RelationTypes.HAS_MANY:
return { icon: RollupIcon, color: 'text-yellow-500' }
case RelationTypes.BELONGS_TO:
return { icon: RollupIcon, color: 'text-sky-500' }
}
return { icon: RollupIcon, color: 'text-grey' }
case UITypes.Count:
return CountIcon
return { icon: CountIcon, color: 'text-grey' }
}
return GenericIcon
return { icon: GenericIcon, color: 'text-grey' }
})
</script>
<template>
<component :is="icon" class="text-grey mx-1 !text-sm" />
<component :is="icon.icon" class="mx-1 !text-sm" :class="icon.color" />
</template>

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

@ -54,6 +54,10 @@ const isManualSaved = $computed(() => {
return [UITypes.Currency, UITypes.Duration].includes(column?.value?.uidt as UITypes)
})
const isPrimary = computed(() => {
return column?.value?.pv
})
const vModel = computed({
get: () => props.modelValue,
set: (val) => {
@ -108,6 +112,7 @@ const syncAndNavigate = (dir: NavigateDir) => {
<template>
<div
class="nc-cell w-full h-full"
:class="{ 'text-blue-600': isPrimary }"
@keydown.stop.left
@keydown.stop.right
@keydown.stop.up

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

@ -293,11 +293,15 @@ const expandForm = (row: Row, state: Record<string, any>) => {
<div class="flex flex-col h-100 min-h-0 w-100">
<div class="nc-grid-wrapper min-h-0 flex-1 scrollbar-thin-dull">
<a-dropdown v-model:visible="contextMenu" :trigger="['contextmenu']">
<table ref="smartTable" class="xc-row-table nc-grid backgroundColorDefault" @contextmenu.prevent="contextMenu = true">
<table
ref="smartTable"
class="xc-row-table nc-grid backgroundColorDefault !h-auto"
@contextmenu.prevent="contextMenu = true"
>
<thead>
<tr class="nc-grid-header border-1 bg-gray-100 sticky top[-1px]">
<th>
<div class="w-full h-full bg-gray-100 flex min-w-[80px] pl-5 pr-1 items-center">
<div class="w-full h-full bg-gray-100 flex min-w-[70px] pl-5 pr-1 items-center">
<div class="nc-no-label text-gray-500" :class="{ hidden: selectedAllRecords }">#</div>
<div
:class="{ hidden: !selectedAllRecords, flex: selectedAllRecords }"
@ -355,9 +359,10 @@ const expandForm = (row: Row, state: Record<string, any>) => {
<template #default="{ state }">
<tr class="nc-grid-row">
<td key="row-index" class="caption nc-grid-cell pl-5 pr-1">
<div class="align-center flex min-w-[80px]">
<div v-if="!readonly" class="nc-row-no" :class="{ hidden: row.rowMeta.selected }">{{ rowIndex + 1 }}</div>
<div v-else>{{ rowIndex + 1 }}</div>
<div class="align-center flex gap-1 min-w-[55px]">
<div v-if="!readonly" class="nc-row-no text-xs text-gray-500" :class="{ hidden: row.rowMeta.selected }">
{{ rowIndex + 1 }}
</div>
<div
v-if="!readonly"
:class="{ hidden: !row.rowMeta.selected, flex: row.rowMeta.selected }"

23
packages/nc-gui-v2/components/smartsheet/expanded-form/Comments.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { enumColor, nextTick, ref, timeAgo, useExpandedFormStoreOrThrow, watch } from '#imports'
import { enumColor, ref, timeAgo, useExpandedFormStoreOrThrow, watch } from '#imports'
const { loadCommentsAndLogs, commentsAndLogs, isCommentsLoading, commentsOnly, saveComment, isYou, comment } =
useExpandedFormStoreOrThrow()
@ -13,52 +13,53 @@ const showborder = ref(false)
watch(
commentsAndLogs,
() => {
nextTick(() => {
// todo: replace setTimeout
setTimeout(() => {
if (commentsWrapperEl.value) commentsWrapperEl.value.scrollTop = commentsWrapperEl.value?.scrollHeight
})
}, 200)
},
{ immediate: true },
)
</script>
<template>
<div class="h-full d-flex flex-column w-full">
<div ref="commentsWrapperEl" class="flex-grow-1 min-h-[100px] overflow-y-auto scrollbar-thin-primary p-2">
<div class="h-full flex flex-col w-full bg-[#eceff1] p-2">
<div ref="commentsWrapperEl" class="flex-grow-1 min-h-[100px] overflow-y-auto scrollbar-thin-primary p-2 space-y-2">
<v-skeleton-loader v-if="isCommentsLoading && !commentsAndLogs" type="list-item-avatar-two-line@8" />
<template v-else>
<div v-for="log of commentsAndLogs" :key="log.id" class="flex gap-1 text-xs">
<MdiAccountCircle class="row-span-2" :class="isYou(log.user) ? 'text-pink-300' : 'text-blue-300 '" />
<div class="flex-grow">
<p class="mb-1 caption edited-text text-[10px] text-gray">
<p class="mb-1 caption edited-text text-[10px] text-gray-500">
{{ isYou(log.user) ? 'You' : log.user == null ? 'Shared base' : log.user }}
{{ log.op_type === 'COMMENT' ? 'commented' : log.op_sub_type === 'INSERT' ? 'created' : 'edited' }}
</p>
<p
v-if="log.op_type === 'COMMENT'"
class="caption mb-0 nc-chip w-full min-h-20px"
class="block caption my-2 nc-chip w-full min-h-20px p-2 rounded"
:style="{ backgroundColor: enumColor.light[2] }"
>
{{ log.description }}
</p>
<p v-else v-dompurify-html="log.details" class="caption mb-0" style="word-break: break-all" />
<p v-else v-dompurify-html="log.details" class="caption my-3" style="word-break: break-all" />
<p class="time text-right text-[10px] mb-0">
<p class="time text-right text-[10px] mb-0 mt-1 text-gray-500">
{{ timeAgo(log.created_at) }}
</p>
</div>
</div>
</template>
</div>
<div class="border-1 my-2 w-full ml-6" />
<div class="border-1 my-2 w-full" />
<div class="p-0">
<div class="flex justify-center">
<a-checkbox v-model:checked="commentsOnly" @change="loadCommentsAndLogs"
><span class="text-[11px] text-gray-500">Comments only</span>
</a-checkbox>
</div>
<div class="flex-shrink-1 mt-2 d-flex pl-4">
<div class="flex-shrink-1 mt-2 d-flex">
<a-input
v-model:value="comment"
class="!text-xs"

6
packages/nc-gui-v2/components/smartsheet/expanded-form/index.vue

@ -102,7 +102,7 @@ export default {
<template>
<a-modal v-model:visible="isExpanded" :footer="null" width="min(90vw,1000px)" :body-style="{ padding: 0 }" :closable="false">
<Header @cancel="isExpanded = false" />
<a-card class="!bg-gray-100">
<div class="!bg-gray-100 rounded">
<div class="flex h-full nc-form-wrapper items-stretch min-h-[70vh]">
<div class="flex-grow overflow-auto scrollbar-thin-primary">
<div class="w-[500px] mx-auto">
@ -131,7 +131,7 @@ export default {
</div>
</div>
</div>
</a-card>
</div>
</a-modal>
</template>
@ -154,7 +154,7 @@ export default {
}
.nc-form-wrapper {
max-height: max(calc(90vh - 100px), 600px);
max-height: max(calc(90vh - 150px), 600px);
height: max-content !important;
}
</style>

2
packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue

@ -31,7 +31,7 @@ export default {
<template>
<div
class="group py-1 px-2 flex align-center gap-1 bg-gray-200/50 hover:bg-gray-200 rounded-[20px]"
class="group py-1 px-2 mr-1 my-1 flex align-center bg-blue-100/60 hover:bg-blue-100/40 rounded-[2px]"
:class="{ active }"
@click="expandedFormDlg = true"
>

50
packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue

@ -198,51 +198,15 @@ const copyAuthToken = async () => {
<a-menu-divider />
<a-menu-item key="teamAndAuth">
<a-menu-item key="teamAndSettings">
<div
v-if="isUIAllowed('teamAndAuth')"
v-t="['c:navdraw:team-and-auth']"
v-if="isUIAllowed('settings')"
v-t="['c:navdraw:project-settings']"
class="nc-project-menu-item group"
@click="toggleDialog(true, 'teamAndAuth')"
>
<MdiAccountGroup class="group-hover:text-pink-500 nc-team-and-auth" />
Team & Auth
</div>
</a-menu-item>
<a-menu-item key="appStore">
<div
v-if="isUIAllowed('appStore')"
v-t="['c:navdraw:app-store']"
class="nc-project-menu-item group"
@click="toggleDialog(true, 'appStore')"
>
<MdiStore class="group-hover:text-pink-500 nc-app-store" />
App Store
</div>
</a-menu-item>
<a-menu-item key="metaData">
<div
v-if="isUIAllowed('projectMetadata')"
v-t="['c:navdraw:project-metadata']"
class="nc-project-menu-item group"
@click="toggleDialog(true, 'metaData')"
>
<MdiTableBorder class="group-hover:text-pink-500 nc-meta-data" />
Project Metadata
</div>
</a-menu-item>
<a-menu-item key="audit">
<div
v-if="isUIAllowed('audit')"
v-t="['c:navdraw:audit']"
class="nc-project-menu-item group"
@click="toggleDialog(true, 'audit')"
>
<MdiNotebookCheckOutline class="group-hover:text-pink-500 nc-audit" />
Audit
<MdiCog class="group-hover:text-pink-500 nc-team-settings" />
Team & Settings
</div>
</a-menu-item>
@ -323,4 +287,8 @@ const copyAuthToken = async () => {
:deep(.ant-dropdown-menu-item) {
@apply !py-0 active:(ring ring-pink-500);
}
:deep(.ant-dropdown-trigger) {
@apply h-full;
}
</style>

Loading…
Cancel
Save