Browse Source

fix(nc-gui): show more records in datetime day view if more than 8

pull/7611/head
DarkPhoenix2704 9 months ago
parent
commit
73316005cc
  1. 12
      packages/nc-gui/components/smartsheet/Cell.vue
  2. 2
      packages/nc-gui/components/smartsheet/Toolbar.vue
  3. 35
      packages/nc-gui/components/smartsheet/calendar/DayView/DateField.vue
  4. 164
      packages/nc-gui/components/smartsheet/calendar/DayView/DateTimeField.vue
  5. 33
      packages/nc-gui/components/smartsheet/calendar/MonthView.vue
  6. 22
      packages/nc-gui/components/smartsheet/calendar/RecordCard.vue
  7. 24
      packages/nc-gui/components/smartsheet/calendar/SideMenu.vue
  8. 6
      packages/nc-gui/components/smartsheet/calendar/SideRecordCard.vue
  9. 13
      packages/nc-gui/components/smartsheet/calendar/VRecordCard.vue
  10. 29
      packages/nc-gui/components/smartsheet/calendar/WeekView/DateField.vue
  11. 29
      packages/nc-gui/components/smartsheet/calendar/WeekView/DateTimeField.vue
  12. 6
      packages/nc-gui/components/smartsheet/calendar/index.vue
  13. 8
      packages/nc-gui/utils/dataUtils.ts

12
packages/nc-gui/components/smartsheet/Cell.vue

@ -1,4 +1,4 @@
<script setup lang="ts"> <script lang="ts" setup>
import type { ColumnType } from 'nocodb-sdk' import type { ColumnType } from 'nocodb-sdk'
import { isSystemColumn } from 'nocodb-sdk' import { isSystemColumn } from 'nocodb-sdk'
import { import {
@ -88,6 +88,8 @@ const isPublic = inject(IsPublicInj, ref(false))
const isSurveyForm = inject(IsSurveyFormInj, ref(false)) const isSurveyForm = inject(IsSurveyFormInj, ref(false))
const isCalendar = inject(IsCalendarInj, ref(false))
const isEditColumnMenu = inject(EditColumnInj, ref(false)) const isEditColumnMenu = inject(EditColumnInj, ref(false))
const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false)) const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))
@ -195,20 +197,20 @@ onUnmounted(() => {
<template> <template>
<div <div
ref="elementToObserve" ref="elementToObserve"
class="nc-cell w-full h-full relative"
:class="[ :class="[
`nc-cell-${(column?.uidt || 'default').toLowerCase()}`, `nc-cell-${(column?.uidt || 'default').toLowerCase()}`,
{ {
'text-brand-500': isPrimary(column) && !props.virtual && !isForm, 'text-brand-500': isPrimary(column) && !props.virtual && !isForm && !isCalendar,
'nc-grid-numeric-cell-right': isGrid && isNumericField && !isEditColumnMenu && !isForm && !isExpandedFormOpen, 'nc-grid-numeric-cell-right': isGrid && isNumericField && !isEditColumnMenu && !isForm && !isExpandedFormOpen,
'h-10': isForm && !isSurveyForm && !isAttachment(column) && !props.virtual, 'h-10': isForm && !isSurveyForm && !isAttachment(column) && !props.virtual,
'nc-grid-numeric-cell-left': (isForm && isNumericField && isExpandedFormOpen) || isEditColumnMenu, 'nc-grid-numeric-cell-left': (isForm && isNumericField && isExpandedFormOpen) || isEditColumnMenu,
'!min-h-30 resize-y': isTextArea(column) && (isForm || isSurveyForm), '!min-h-30 resize-y': isTextArea(column) && (isForm || isSurveyForm),
}, },
]" ]"
class="nc-cell w-full h-full relative"
@contextmenu="onContextmenu"
@keydown.enter.exact="navigate(NavigateDir.NEXT, $event)" @keydown.enter.exact="navigate(NavigateDir.NEXT, $event)"
@keydown.shift.enter.exact="navigate(NavigateDir.PREV, $event)" @keydown.shift.enter.exact="navigate(NavigateDir.PREV, $event)"
@contextmenu="onContextmenu"
> >
<template v-if="column"> <template v-if="column">
<template v-if="intersected"> <template v-if="intersected">
@ -260,7 +262,7 @@ onUnmounted(() => {
</div> </div>
</template> </template>
<style scoped lang="scss"> <style lang="scss" scoped>
.nc-grid-numeric-cell-left { .nc-grid-numeric-cell-left {
text-align: left; text-align: left;
:deep(input) { :deep(input) {

2
packages/nc-gui/components/smartsheet/Toolbar.vue

@ -51,7 +51,7 @@ const { allowCSVDownload } = useSharedView()
<LazySmartsheetToolbarCalendarRange v-if="isCalendar" /> <LazySmartsheetToolbarCalendarRange v-if="isCalendar" />
<LazySmartsheetToolbarFieldsMenu <LazySmartsheetToolbarFieldsMenu
v-if="isGrid || isGallery || isKanban || isMap || !isCalendar" v-if="isGrid || isGallery || isKanban || isMap || isCalendar"
:show-system-fields="false" :show-system-fields="false"
/> />

35
packages/nc-gui/components/smartsheet/calendar/DayView/DateField.vue

@ -1,18 +1,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { UITypes, isVirtualCol } from 'nocodb-sdk'
import { type Row, computed, ref } from '#imports' import { type Row, computed, ref } from '#imports'
import { isRowEmpty } from '~/utils'
const emit = defineEmits(['expand-record', 'new-record']) const emit = defineEmits(['expand-record', 'new-record'])
const meta = inject(MetaInj, ref()) const meta = inject(MetaInj, ref())
const fields = inject(FieldsInj, ref([]))
const container = ref() const container = ref()
const { isUIAllowed } = useRoles() const { isUIAllowed } = useRoles()
const displayField = computed(() => meta.value?.columns?.find((c) => c.pv && fields.value.includes(c)) ?? null) const { selectedDate, formattedData, formattedSideBarData, calendarRange, updateRowProperty, displayField } =
useCalendarViewStoreOrThrow()
const { selectedDate, formattedData, formattedSideBarData, calendarRange, updateRowProperty } = useCalendarViewStoreOrThrow()
const recordsAcrossAllRange = computed<Row[]>(() => { const recordsAcrossAllRange = computed<Row[]>(() => {
let dayRecordCount = 0 let dayRecordCount = 0
@ -178,14 +178,37 @@ const dropEvent = (event: DragEvent) => {
<div v-for="(record, rowIndex) in recordsAcrossAllRange" :key="rowIndex" :style="record.rowMeta.style" class="absolute mt-2"> <div v-for="(record, rowIndex) in recordsAcrossAllRange" :key="rowIndex" :style="record.rowMeta.style" class="absolute mt-2">
<LazySmartsheetRow :row="record"> <LazySmartsheetRow :row="record">
<LazySmartsheetCalendarRecordCard <LazySmartsheetCalendarRecordCard
:name="record.row[displayField!.title!]"
:position="record.rowMeta.position" :position="record.rowMeta.position"
:record="record" :record="record"
:resize="false" :resize="false"
color="blue" color="blue"
size="small" size="small"
@click="emit('expand-record', record)" @click="emit('expand-record', record)"
/> >
<template v-if="!isRowEmpty(record, displayField)">
<div
:class="{
'!mt-2': displayField.uidt === UITypes.SingleLineText,
'!mt-1': displayField.uidt === UITypes.MultiSelect || displayField.uidt === UITypes.SingleSelect,
}"
>
<LazySmartsheetVirtualCell
v-if="isVirtualCol(displayField)"
v-model="record.row[displayField.title]"
:column="displayField"
:row="record"
/>
<LazySmartsheetCell
v-else
v-model="record.row[displayField.title]"
:column="displayField"
:edit-enabled="false"
:read-only="true"
/>
</div>
</template>
</LazySmartsheetCalendarRecordCard>
</LazySmartsheetRow> </LazySmartsheetRow>
</div> </div>
</div> </div>

164
packages/nc-gui/components/smartsheet/calendar/DayView/DateTimeField.vue

@ -1,18 +1,16 @@
<script lang="ts" setup> <script lang="ts" setup>
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { UITypes, isVirtualCol } from 'nocodb-sdk'
import { type Row, computed, ref } from '#imports' import { type Row, computed, ref } from '#imports'
import { isRowEmpty } from '~/utils'
const emit = defineEmits(['expandRecord', 'new-record']) const emit = defineEmits(['expandRecord', 'new-record'])
const meta = inject(MetaInj, ref()) const meta = inject(MetaInj, ref())
const fields = inject(FieldsInj, ref([]))
const container = ref() const container = ref()
const { isUIAllowed } = useRoles() const { isUIAllowed } = useRoles()
const displayField = computed(() => meta.value?.columns?.find((c) => c.pv && fields.value.includes(c)) ?? null) const { selectedDate, selectedTime, formattedData, calendarRange, formattedSideBarData, updateRowProperty, displayField } =
const { selectedDate, selectedTime, formattedData, calendarRange, formattedSideBarData, updateRowProperty } =
useCalendarViewStoreOrThrow() useCalendarViewStoreOrThrow()
const hours = computed(() => { const hours = computed(() => {
@ -20,16 +18,7 @@ const hours = computed(() => {
const _selectedDate = dayjs(selectedDate.value) const _selectedDate = dayjs(selectedDate.value)
for (let i = 0; i < 24; i++) { for (let i = 0; i < 24; i++) {
hours.push( hours.push(_selectedDate.clone().startOf('day').add(i, 'hour'))
dayjs()
.hour(i)
.minute(0)
.second(0)
.millisecond(0)
.year(_selectedDate.year())
.month(_selectedDate.month())
.date(_selectedDate.date()),
)
} }
return hours return hours
}) })
@ -40,17 +29,30 @@ function getRandomNumbers() {
return randomValues.join('') return randomValues.join('')
} }
const recordsAcrossAllRange = computed<Row[]>(() => { const recordsAcrossAllRange = computed<{
record: Row[]
count: {
[key: string]: {
id: string
overflow: boolean
overflowCount: number
}
}
}>(() => {
const scheduleStart = dayjs(selectedDate.value).startOf('day') const scheduleStart = dayjs(selectedDate.value).startOf('day')
const scheduleEnd = dayjs(selectedDate.value).endOf('day') const scheduleEnd = dayjs(selectedDate.value).endOf('day')
const overlaps: { const overlaps: {
[key: string]: string[] [key: string]: {
id: string[]
overflow: boolean
overflowCount: number
}
} = {} } = {}
const perRecordHeight = 80 const perRecordHeight = 80
if (!calendarRange.value) return [] if (!calendarRange.value) return { record: [], count: {} }
let recordsByRange: Array<Row> = [] let recordsByRange: Array<Row> = []
@ -96,19 +98,28 @@ const recordsAcrossAllRange = computed<Row[]>(() => {
const heightInPixels = Math.max((endDate.diff(startDate, 'minute') / 60) * 80, perRecordHeight) const heightInPixels = Math.max((endDate.diff(startDate, 'minute') / 60) * 80, perRecordHeight)
const startHour = startDate.hour() const startHour = startDate.hour()
const endHour = endDate.hour()
let startMinutes = startDate.minute() + startHour * 60 let _startDate = startDate.clone()
const endMinutes = endDate.minute() + endHour * 60 while (_startDate.isBefore(endDate)) {
const timeKey = _startDate.format('HH:mm')
if (!overlaps[timeKey]) {
overlaps[timeKey] = {
id: [],
overflow: false,
overflowCount: 0,
}
}
overlaps[timeKey].id.push(id)
while (startMinutes < endMinutes) { if (overlaps[timeKey].id.length > 4) {
if (!overlaps[startMinutes]) { overlaps[timeKey].overflow = true
overlaps[startMinutes] = [] style.display = 'none'
overlaps[timeKey].overflowCount += 1
} }
overlaps[startMinutes].push(id) _startDate = _startDate.add(15, 'minutes')
startMinutes += 15
} }
const finalTopInPixels = topInPixels + 5 + startHour * 2 const finalTopInPixels = topInPixels + 5 + startHour * 2
const style: Partial<CSSStyleDeclaration> = { const style: Partial<CSSStyleDeclaration> = {
@ -149,20 +160,35 @@ const recordsAcrossAllRange = computed<Row[]>(() => {
const id = getRandomNumbers() const id = getRandomNumbers()
const startDate = dayjs(record.row[fromCol.title!]) const startDate = dayjs(record.row[fromCol.title!])
const endDate = dayjs(record.row[fromCol.title!]).add(30, 'minutes') const endDate = dayjs(record.row[fromCol.title!]).add(15, 'minutes')
const startHour = startDate.hour() const startHour = startDate.hour()
const endHour = endDate.hour()
let startMinutes = startDate.minute() + startHour * 60 let style: Partial<CSSStyleDeclaration> = {}
const endMinutes = endDate.minute() + endHour * 60 let _startDate = startDate.clone()
while (_startDate.isBefore(endDate)) {
const timeKey = _startDate.format('HH:mm')
console.log('timeKey', timeKey)
while (startMinutes < endMinutes) { if (!overlaps[timeKey]) {
if (!overlaps[startMinutes]) { overlaps[timeKey] = {
overlaps[startMinutes] = [] id: [],
overflow: false,
overflowCount: 0,
}
} }
overlaps[startMinutes].push(id) overlaps[timeKey].id.push(id)
startMinutes += 10
if (overlaps[timeKey].id.length > 8) {
overlaps[timeKey].overflow = true
overlaps[timeKey].overflowCount += 1
style = {
...style,
display: 'none',
}
}
_startDate = _startDate.add(15, 'minutes')
} }
const topInPixels = (startDate.hour() + startDate.minute() / 60) * 80 const topInPixels = (startDate.hour() + startDate.minute() / 60) * 80
@ -170,15 +196,18 @@ const recordsAcrossAllRange = computed<Row[]>(() => {
const finalTopInPixels = topInPixels + startHour const finalTopInPixels = topInPixels + startHour
style = {
...style,
top: `${finalTopInPixels}px`,
height: `${heightInPixels}px`,
}
recordsByRange.push({ recordsByRange.push({
...record, ...record,
rowMeta: { rowMeta: {
...record.rowMeta, ...record.rowMeta,
range: range as any, range: range as any,
style: { style,
top: `${finalTopInPixels}px`,
height: `${heightInPixels + 5}px`,
},
id, id,
position: 'rounded', position: 'rounded',
}, },
@ -191,10 +220,9 @@ const recordsAcrossAllRange = computed<Row[]>(() => {
let maxOverlaps = 1 let maxOverlaps = 1
let overlapIndex = 0 let overlapIndex = 0
for (const minutes in overlaps) { for (const minutes in overlaps) {
if (overlaps[minutes].includes(record.rowMeta.id!)) { if (overlaps[minutes].id.includes(record.rowMeta.id!)) {
maxOverlaps = Math.max(maxOverlaps, overlaps[minutes].length) maxOverlaps = Math.max(maxOverlaps, overlaps[minutes].id.length - overlaps[minutes].overflowCount)
overlapIndex = Math.max(overlaps[minutes].id.indexOf(record.rowMeta.id!), overlapIndex)
overlapIndex = Math.max(overlaps[minutes].indexOf(record.rowMeta.id!), overlapIndex)
} }
} }
@ -210,7 +238,10 @@ const recordsAcrossAllRange = computed<Row[]>(() => {
return record return record
}) })
return recordsByRange return {
count: overlaps,
record: recordsByRange,
}
}) })
const dragRecord = ref<Row | null>(null) const dragRecord = ref<Row | null>(null)
@ -524,7 +555,7 @@ const dragStart = (event: MouseEvent, record: Row) => {
<template> <template>
<div <div
v-if="recordsAcrossAllRange.length" v-if="recordsAcrossAllRange.record.length"
ref="container" ref="container"
class="w-full relative no-selection h-[calc(100vh-10rem)] overflow-y-auto nc-scrollbar-md" class="w-full relative no-selection h-[calc(100vh-10rem)] overflow-y-auto nc-scrollbar-md"
> >
@ -621,11 +652,25 @@ const dragStart = (event: MouseEvent, record: Row) => {
> >
<component :is="iconMap.plus" class="h-4 w-4" /> <component :is="iconMap.plus" class="h-4 w-4" />
</NcButton> </NcButton>
<NcButton
v-if="recordsAcrossAllRange?.count?.[hour.format('HH:mm')]?.overflow"
class="!absolute bottom-2 text-center w-15 mx-auto inset-x-0 z-3 text-gray-500"
size="xxsmall"
type="secondary"
@click="viewMore(hour)"
>
<span class="text-xs">
+
{{ recordsAcrossAllRange?.count[hour.format('HH:mm')]?.overflowCount }}
more
</span>
</NcButton>
</div> </div>
<div class="absolute inset-0 pointer-events-none"> <div class="absolute inset-0 pointer-events-none">
<div class="relative !ml-[60px]"> <div class="relative !ml-[60px]">
<div <div
v-for="(record, rowIndex) in recordsAcrossAllRange" v-for="(record, rowIndex) in recordsAcrossAllRange.record"
:key="rowIndex" :key="rowIndex"
:data-unique-id="record.rowMeta.id" :data-unique-id="record.rowMeta.id"
:style="record.rowMeta.style" :style="record.rowMeta.style"
@ -635,13 +680,36 @@ const dragStart = (event: MouseEvent, record: Row) => {
> >
<LazySmartsheetRow :row="record"> <LazySmartsheetRow :row="record">
<LazySmartsheetCalendarVRecordCard <LazySmartsheetCalendarVRecordCard
:name="record.row![displayField!.title!]"
:position="record.rowMeta!.position" :position="record.rowMeta!.position"
:record="record" :record="record"
:resize="!!record.rowMeta.range?.fk_to_col && isUIAllowed('dataEdit')" :resize="!!record.rowMeta.range?.fk_to_col && isUIAllowed('dataEdit')"
color="blue" color="blue"
@resize-start="onResizeStart" @resize-start="onResizeStart"
/> >
<template v-if="!isRowEmpty(record, displayField)">
<div
:class="{
'!mt-2': displayField.uidt === UITypes.SingleLineText,
'!mt-1': displayField.uidt === UITypes.MultiSelect || displayField.uidt === UITypes.SingleSelect,
}"
>
<LazySmartsheetVirtualCell
v-if="isVirtualCol(displayField)"
v-model="record.row[displayField.title]"
:column="displayField"
:row="record"
/>
<LazySmartsheetCell
v-else
v-model="record.row[displayField.title]"
:column="displayField"
:edit-enabled="false"
:read-only="true"
/>
</div>
</template>
</LazySmartsheetCalendarVRecordCard>
</LazySmartsheetRow> </LazySmartsheetRow>
</div> </div>
</div> </div>

33
packages/nc-gui/components/smartsheet/calendar/MonthView.vue

@ -1,7 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { UITypes, isVirtualCol } from 'nocodb-sdk'
import type { Row } from '#imports' import type { Row } from '#imports'
import { isRowEmpty } from '~/utils'
const emit = defineEmits(['new-record', 'expandRecord']) const emit = defineEmits(['new-record', 'expandRecord'])
@ -151,7 +153,7 @@ const recordsToDisplay = computed<{
const recordIndex = recordsInDay[dateKey].count const recordIndex = recordsInDay[dateKey].count
const top = weekIndex * perHeight + spaceBetweenRecords + (recordIndex - 1) * perRecordHeight const top = weekIndex * perHeight + spaceBetweenRecords + (recordIndex - 1) * perRecordHeight
const heightRequired = perRecordHeight * recordIndex + spaceBetweenRecords const heightRequired = perRecordHeight * recordIndex + spaceBetweenRecords + 25
if (heightRequired > perHeight) { if (heightRequired > perHeight) {
style.display = 'none' style.display = 'none'
@ -726,7 +728,7 @@ const isDateSelected = (date: dayjs.Dayjs) => {
:class="{ :class="{
'border-brand-500 border-1 border-r-1 border-b-1': 'border-brand-500 border-1 border-r-1 border-b-1':
isDateSelected(day) || (focusedDate && dayjs(day).isSame(focusedDate, 'day')), isDateSelected(day) || (focusedDate && dayjs(day).isSame(focusedDate, 'day')),
'!text-gray-200': !isDayInPagedMonth(day), '!text-gray-400': !isDayInPagedMonth(day),
}" }"
class="text-right relative group text-sm h-full border-r-1 border-b-1 border-gray-200 font-medium hover:bg-gray-50 text-gray-800 bg-white" class="text-right relative group text-sm h-full border-r-1 border-b-1 border-gray-200 font-medium hover:bg-gray-50 text-gray-800 bg-white"
@click="selectDate(day)" @click="selectDate(day)"
@ -847,7 +849,6 @@ const isDateSelected = (date: dayjs.Dayjs) => {
<LazySmartsheetRow :row="record"> <LazySmartsheetRow :row="record">
<LazySmartsheetCalendarRecordCard <LazySmartsheetCalendarRecordCard
:hover="hoverRecord === record.rowMeta.id" :hover="hoverRecord === record.rowMeta.id"
:name="record.row[displayField!.title!]"
:position="record.rowMeta.position" :position="record.rowMeta.position"
:record="record" :record="record"
:resize="!!record.rowMeta.range?.fk_to_col && isUIAllowed('dataEdit')" :resize="!!record.rowMeta.range?.fk_to_col && isUIAllowed('dataEdit')"
@ -860,7 +861,31 @@ const isDateSelected = (date: dayjs.Dayjs) => {
" "
@resize-start="onResizeStart" @resize-start="onResizeStart"
@dblclick.stop="emit('expand-record', record)" @dblclick.stop="emit('expand-record', record)"
/> >
<template v-if="!isRowEmpty(record, displayField)">
<div
:class="{
'!mt-2': displayField.uidt === UITypes.SingleLineText,
'!mt-1': displayField.uidt === UITypes.MultiSelect || displayField.uidt === UITypes.SingleSelect,
}"
>
<LazySmartsheetVirtualCell
v-if="isVirtualCol(displayField)"
v-model="record.row[displayField.title]"
:column="displayField"
:row="record"
/>
<LazySmartsheetCell
v-else
v-model="record.row[displayField.title]"
:column="displayField"
:edit-enabled="false"
:read-only="true"
/>
</div>
</template>
</LazySmartsheetCalendarRecordCard>
</LazySmartsheetRow> </LazySmartsheetRow>
</div> </div>
</div> </div>

22
packages/nc-gui/components/smartsheet/calendar/RecordCard.vue

@ -1,26 +1,19 @@
<script lang="ts" setup> <script lang="ts" setup>
interface Props { interface Props {
record: Record<string, string>
name: string
date?: string
color?: string color?: string
resize?: boolean resize?: boolean
hover?: boolean hover?: boolean
selected?: boolean selected?: boolean
size?: 'small' | 'medium' | 'large' | 'auto' size?: 'small' | 'medium' | 'large' | 'auto'
showDate?: boolean
position?: 'leftRounded' | 'rightRounded' | 'rounded' | 'none' position?: 'leftRounded' | 'rightRounded' | 'rounded' | 'none'
} }
const props = withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {
name: '',
date: '',
resize: true, resize: true,
selected: false, selected: false,
hover: false, hover: false,
color: 'blue', color: 'blue',
size: 'small', size: 'small',
showDate: true,
position: 'rounded', position: 'rounded',
}) })
@ -34,9 +27,9 @@ const emit = defineEmits(['resize-start'])
'min-h-10': size === 'medium', 'min-h-10': size === 'medium',
'min-h-12': size === 'large', 'min-h-12': size === 'large',
'h-full': size === 'auto', 'h-full': size === 'auto',
'rounded-l-lg ml-3': position === 'leftRounded', 'rounded-l-lg ml-1': position === 'leftRounded',
'rounded-r-lg mr-3': position === 'rightRounded', 'rounded-r-lg mr-1': position === 'rightRounded',
'rounded-lg mx-3': position === 'rounded', 'rounded-lg mx-1': position === 'rounded',
'rounded-none': position === 'none', 'rounded-none': position === 'none',
'bg-maroon-50': color === 'maroon', 'bg-maroon-50': color === 'maroon',
'bg-blue-50': color === 'blue', 'bg-blue-50': color === 'blue',
@ -76,10 +69,11 @@ const emit = defineEmits(['resize-start'])
</NcButton> </NcButton>
</div> </div>
<div class="ml-3 mt-2 pr-3 text-ellipsis overflow-hidden w-full h-6 absolute"> <div class="ml-3 pr-3 text-ellipsis overflow-hidden w-full h-8 absolute">
<span v-if="position === 'rightRounded' || position === 'none'"> .... </span> <span v-if="position === 'rightRounded' || position === 'none'"> .... </span>
<span class="text-sm text-gray-800">{{ name }}</span> <span class="text-sm text-gray-800">
<span v-if="showDate" class="text-xs ml-1 text-gray-600">{{ date }}</span> <slot />
</span>
<span v-if="position === 'leftRounded' || position === 'none'" class="absolute my-0 right-5"> .... </span> <span v-if="position === 'leftRounded' || position === 'none'" class="absolute my-0 right-5"> .... </span>
</div> </div>
<div <div

24
packages/nc-gui/components/smartsheet/calendar/SideMenu.vue

@ -1,8 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VNodeRef } from '@vue/runtime-core' import type { VNodeRef } from '@vue/runtime-core'
import { UITypes } from 'nocodb-sdk' import { UITypes, isVirtualCol } from 'nocodb-sdk'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { type Row, computed, ref } from '#imports' import { type Row, computed, isRowEmpty, ref } from '#imports'
const props = defineProps<{ const props = defineProps<{
visible: boolean visible: boolean
@ -338,7 +338,6 @@ const sideBarListScrollHandle = useDebounceFn(async (e: Event) => {
dayjs(record.row[record.rowMeta.range!.fk_to_col.title!]), dayjs(record.row[record.rowMeta.range!.fk_to_col.title!]),
) )
" "
:name="record.row[displayField!.title!]"
:row="record" :row="record"
:to-date=" :to-date="
record.rowMeta.range!.fk_to_col record.rowMeta.range!.fk_to_col
@ -351,7 +350,24 @@ const sideBarListScrollHandle = useDebounceFn(async (e: Event) => {
@click="emit('expand-record', record)" @click="emit('expand-record', record)"
@dragstart="dragStart($event, record)" @dragstart="dragStart($event, record)"
@dragover.prevent @dragover.prevent
/> >
<template v-if="!isRowEmpty(record, displayField)">
<LazySmartsheetVirtualCell
v-if="isVirtualCol(displayField)"
v-model="record.row[displayField.title]"
:column="displayField"
:row="record"
/>
<LazySmartsheetCell
v-else
v-model="record.row[displayField.title]"
:column="displayField"
:edit-enabled="false"
:read-only="true"
/>
</template>
</LazySmartsheetCalendarSideRecordCard>
</LazySmartsheetRow> </LazySmartsheetRow>
</template> </template>
</div> </div>

6
packages/nc-gui/components/smartsheet/calendar/SideRecordCard.vue

@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
interface Props { interface Props {
name: string
fromDate?: string fromDate?: string
toDate?: string toDate?: string
color?: string color?: string
@ -9,7 +8,6 @@ interface Props {
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
name: '',
fromDate: '', fromDate: '',
color: 'blue', color: 'blue',
showDate: true, showDate: true,
@ -32,7 +30,9 @@ const props = withDefaults(defineProps<Props>(), {
class="block h-10 w-1 rounded" class="block h-10 w-1 rounded"
></span> ></span>
<div class="flex flex-col gap-1 ml-3"> <div class="flex flex-col gap-1 ml-3">
<span class="text-sm max-w-36 truncate text-gray-800">{{ name }}</span> <span class="text-sm max-w-36 h-8 truncate text-gray-800">
<slot />
</span>
<span v-if="showDate" class="text-xs text-gray-500">{{ fromDate }} {{ toDate ? ` - ${toDate}` : '' }}</span> <span v-if="showDate" class="text-xs text-gray-500">{{ fromDate }} {{ toDate ? ` - ${toDate}` : '' }}</span>
</div> </div>
</div> </div>

13
packages/nc-gui/components/smartsheet/calendar/VRecordCard.vue

@ -1,22 +1,16 @@
<script lang="ts" setup> <script lang="ts" setup>
interface Props { interface Props {
record: Record<string, string> record: Record<string, string>
name: string
date?: string
color?: string color?: string
resize?: boolean resize?: boolean
selected?: boolean selected?: boolean
showDate?: boolean
position?: 'topRounded' | 'bottomRounded' | 'rounded' | 'none' position?: 'topRounded' | 'bottomRounded' | 'rounded' | 'none'
} }
withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {
name: '',
date: '',
resize: true, resize: true,
selected: false, selected: false,
color: 'blue', color: 'blue',
showDate: true,
position: 'rounded', position: 'rounded',
}) })
@ -73,9 +67,10 @@ const emit = defineEmits(['resize-start'])
<div v-if="position === 'bottomRounded' || position === 'none'" class="ml-3">....</div> <div v-if="position === 'bottomRounded' || position === 'none'" class="ml-3">....</div>
<div class="ml-3 mt-2 pr-3 text-ellipsis overflow-hidden w-full h-6 absolute"> <div class="ml-3 pr-3 text-ellipsis overflow-hidden w-full h-8 absolute">
<span class="text-sm text-gray-800">{{ name }}</span> <span class="text-sm text-gray-800">
<span v-if="showDate" class="text-xs ml-1 text-gray-600">{{ date }}</span> <slot />
</span>
</div> </div>
<div v-if="position === 'topRounded' || position === 'none'" class="h-full pb-7 flex items-end ml-3">....</div> <div v-if="position === 'topRounded' || position === 'none'" class="h-full pb-7 flex items-end ml-3">....</div>
</div> </div>

29
packages/nc-gui/components/smartsheet/calendar/WeekView/DateField.vue

@ -1,7 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { UITypes, isVirtualCol } from 'nocodb-sdk'
import type { Row } from '~/lib' import type { Row } from '~/lib'
import { ref } from '#imports' import { ref } from '#imports'
import { isRowEmpty } from '~/utils'
const emits = defineEmits(['expandRecord']) const emits = defineEmits(['expandRecord'])
@ -614,14 +616,37 @@ const dropEvent = (event: DragEvent) => {
> >
<LazySmartsheetRow :row="record"> <LazySmartsheetRow :row="record">
<LazySmartsheetCalendarRecordCard <LazySmartsheetCalendarRecordCard
:name="record.row[displayField!.title!]"
:position="record.rowMeta.position" :position="record.rowMeta.position"
:record="record" :record="record"
:resize="!!record.rowMeta.range?.fk_to_col && isUIAllowed('dataEdit')" :resize="!!record.rowMeta.range?.fk_to_col && isUIAllowed('dataEdit')"
color="blue" color="blue"
@dblclick="emits('expand-record', record)" @dblclick="emits('expand-record', record)"
@resize-start="onResizeStart" @resize-start="onResizeStart"
/> >
<template v-if="!isRowEmpty(record, displayField)">
<div
:class="{
'!mt-2': displayField.uidt === UITypes.SingleLineText,
'!mt-1': displayField.uidt === UITypes.MultiSelect || displayField.uidt === UITypes.SingleSelect,
}"
>
<LazySmartsheetVirtualCell
v-if="isVirtualCol(displayField)"
v-model="record.row[displayField.title]"
:column="displayField"
:row="record"
/>
<LazySmartsheetCell
v-else
v-model="record.row[displayField.title]"
:column="displayField"
:edit-enabled="false"
:read-only="true"
/>
</div>
</template>
</LazySmartsheetCalendarRecordCard>
</LazySmartsheetRow> </LazySmartsheetRow>
</div> </div>
</div> </div>

29
packages/nc-gui/components/smartsheet/calendar/WeekView/DateTimeField.vue

@ -1,7 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { UITypes, isVirtualCol } from 'nocodb-sdk'
import type { Row } from '~/lib' import type { Row } from '~/lib'
import { computed, ref } from '#imports' import { computed, ref } from '#imports'
import { isRowEmpty } from '~/utils'
const emits = defineEmits(['expandRecord']) const emits = defineEmits(['expandRecord'])
@ -774,13 +776,36 @@ const viewMore = (hour: dayjs.Dayjs) => {
> >
<LazySmartsheetRow :row="record"> <LazySmartsheetRow :row="record">
<LazySmartsheetCalendarVRecordCard <LazySmartsheetCalendarVRecordCard
:name="record.row![displayField!.title!]"
:position="record.rowMeta!.position" :position="record.rowMeta!.position"
:record="record" :record="record"
:resize="!!record.rowMeta.range?.fk_to_col && isUIAllowed('dataEdit')" :resize="!!record.rowMeta.range?.fk_to_col && isUIAllowed('dataEdit')"
color="blue" color="blue"
@resize-start="onResizeStart" @resize-start="onResizeStart"
/> >
<template v-if="!isRowEmpty(record, displayField)">
<div
:class="{
'!mt-2': displayField.uidt === UITypes.SingleLineText,
'!mt-1': displayField.uidt === UITypes.MultiSelect || displayField.uidt === UITypes.SingleSelect,
}"
>
<LazySmartsheetVirtualCell
v-if="isVirtualCol(displayField)"
v-model="record.row[displayField.title]"
:column="displayField"
:row="record"
/>
<LazySmartsheetCell
v-else
v-model="record.row[displayField.title]"
:column="displayField"
:edit-enabled="false"
:read-only="true"
/>
</div>
</template>
</LazySmartsheetCalendarVRecordCard>
</LazySmartsheetRow> </LazySmartsheetRow>
</div> </div>
</div> </div>

6
packages/nc-gui/components/smartsheet/calendar/index.vue

@ -164,10 +164,8 @@ const headerText = computed(() => {
size="small" size="small"
type="secondary" type="secondary"
> >
<div class="flex gap-2 justify-between items-center"> <span class="font-bold text-center text-brand-500">{{ headerText }}</span>
<span class="font-bold text-center text-brand-500">{{ headerText }}</span> <component :is="iconMap.arrowDown" class="h-4 w-4 text-gray-700" />
<component :is="iconMap.arrowDown" class="h-4 w-4 text-gray-700" />
</div>
</NcButton> </NcButton>
<template #overlay> <template #overlay>

8
packages/nc-gui/utils/dataUtils.ts

@ -114,3 +114,11 @@ export const rowDefaultData = (columns: ColumnType[] = []) => {
return defaultData return defaultData
} }
export const isRowEmpty = (record: any, col: any) => {
if (!record || !col) return true
const val = record.row[col.title]
if (!val) return true
return Array.isArray(val) && val.length === 0
}

Loading…
Cancel
Save