Browse Source

Merge pull request #7734 from nocodb/nc-feat/year-view-refresh

feat(nc-gui): Calendar Year View Updates
pull/7802/head
Raju Udava 8 months ago committed by GitHub
parent
commit
791c674c57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 82
      packages/nc-gui/components/nc/DateWeekSelector.vue
  2. 4
      packages/nc-gui/components/nc/MonthYearSelector.vue
  3. 102
      packages/nc-gui/components/smartsheet/calendar/SideMenu.vue
  4. 2
      packages/nc-gui/components/smartsheet/calendar/SideRecordCard.vue
  5. 49
      packages/nc-gui/components/smartsheet/calendar/YearView.vue
  6. 10
      packages/nc-gui/components/smartsheet/calendar/index.vue
  7. 14
      packages/nc-gui/composables/useCalendarViewStore.ts
  8. 2
      packages/nc-gui/utils/browserUtils.ts

82
packages/nc-gui/components/nc/DateWeekSelector.vue

@ -2,6 +2,7 @@
import dayjs from 'dayjs'
interface Props {
size?: 'medium' | 'large' | 'small'
selectedDate?: dayjs.Dayjs | null
isDisabled?: boolean
pageDate?: dayjs.Dayjs
@ -16,6 +17,7 @@ interface Props {
}
const props = withDefaults(defineProps<Props>(), {
size: 'large',
selectedDate: null,
isDisabled: false,
isMondayFirst: true,
@ -135,7 +137,13 @@ const paginate = (action: 'next' | 'prev') => {
</script>
<template>
<div class="px-4 pt-3 pb-4 flex flex-col gap-4">
<div
:class="{
'gap-1': size === 'small',
'gap-4': size === 'medium' || size === 'large',
}"
class="flex flex-col"
>
<div
:class="{
'justify-center': disablePagination,
@ -144,7 +152,7 @@ const paginate = (action: 'next' | 'prev') => {
class="flex items-center"
>
<NcTooltip>
<NcButton v-if="!disablePagination" size="small" type="secondary" @click="paginate('prev')">
<NcButton v-if="!disablePagination" size="medium" type="secondary" @click="paginate('prev')">
<component :is="iconMap.doubleLeftArrow" class="h-4 w-4" />
</NcButton>
<template #title>
@ -152,9 +160,16 @@ const paginate = (action: 'next' | 'prev') => {
</template>
</NcTooltip>
<span class="font-bold text-gray-700">{{ currentMonthYear }}</span>
<span
:class="{
'text-xs': size === 'small',
'text-sm': size === 'medium',
}"
class="font-bold text-gray-700"
>{{ currentMonthYear }}</span
>
<NcTooltip>
<NcButton v-if="!disablePagination" size="small" type="secondary" @click="paginate('next')">
<NcButton v-if="!disablePagination" size="medium" type="secondary" @click="paginate('next')">
<component :is="iconMap.doubleRightArrow" class="h-4 w-4" />
</NcButton>
<template #title>
@ -162,13 +177,41 @@ const paginate = (action: 'next' | 'prev') => {
</template>
</NcTooltip>
</div>
<div class="border-1 border-gray-200 rounded-y-xl max-w-[320px]">
<div class="flex flex-row bg-gray-100 gap-2 rounded-t-xl justify-between px-2">
<span v-for="(day, index) in days" :key="index" class="h-9 flex items-center justify-center w-9 text-gray-500">{{
day
}}</span>
<div
:class="{
'rounded-lg': size === 'small',
'rounded-y-xl': size !== 'small',
}"
class="border-1 border-gray-200 max-w-[320px]"
>
<div
:class="{
'gap-1 px-1': size === 'medium',
'gap-2 px-2': size === 'large',
'px-2 py-1 !rounded-t-lg': size === 'small',
'rounded-t-xl': size !== 'small',
}"
class="flex flex-row bg-gray-100 justify-between"
>
<span
v-for="(day, index) in days"
:key="index"
:class="{
'w-9 h-9': size === 'large',
'w-8 h-8': size === 'medium',
'text-[10px]': size === 'small',
}"
class="flex items-center uppercase justify-center text-gray-500"
>{{ day[0] }}</span
>
</div>
<div class="grid grid-cols-7 gap-2 p-2">
<div
:class="{
'gap-2 p-2': size === 'large',
'gap-1 p-1': size === 'medium',
}"
class="grid grid-cols-7"
>
<span
v-for="(date, index) in dates"
:key="index"
@ -177,16 +220,31 @@ const paginate = (action: 'next' | 'prev') => {
'bg-brand-50 border-1 !border-brand-500': isSelectedDate(date) && !isWeekPicker && isDayInPagedMonth(date),
'hover:(border-1 border-gray-200 bg-gray-100)': !isSelectedDate(date) && !isWeekPicker,
'nc-selected-week z-1': isDateInSelectedWeek(date) && isWeekPicker,
'border-none': isWeekPicker,
'border-transparent': !isWeekPicker,
'text-gray-400': !isDateInCurrentMonth(date),
'nc-selected-week-start': isSameDate(date, selectedWeek?.start),
'nc-selected-week-end': isSameDate(date, selectedWeek?.end),
'rounded-md bg-brand-50 nc-calendar-today text-brand-500': isSameDate(date, dayjs()) && isDateInCurrentMonth(date),
'h-9 w-9': size === 'large',
'h-8 w-8': size === 'medium',
'h-6 w-6 text-[10px]': size === 'small',
}"
class="h-9 w-9 px-1 py-2 relative font-medium flex items-center cursor-pointer justify-center"
class="px-1 py-1 relative border-1 font-large flex items-center cursor-pointer justify-center"
data-testid="nc-calendar-date"
@click="handleSelectDate(date)"
>
<span v-if="isActiveDate(date)" class="absolute z-2 h-1.5 w-1.5 rounded-full bg-brand-500 top-1 right-1"></span>
<span
v-if="isActiveDate(date)"
:class="{
'h-1.5 w-1.5': size === 'large',
'h-1 w-1': size === 'medium',
'h-0.75 w-0.75': size === 'small',
'top-1 right-1': size !== 'small',
'top-0.5 right-0.5': size === 'small',
}"
class="absolute z-2 rounded-full bg-brand-500"
></span>
<span class="z-2">
{{ date.get('date') }}
</span>

4
packages/nc-gui/components/nc/MonthYearSelector.vue

@ -114,7 +114,7 @@ const compareYear = (date1: dayjs.Dayjs, date2: dayjs.Dayjs) => {
:class="{
'!bg-brand-50 border-1 !border-brand-500': isMonthSelected(month),
}"
class="h-9 rounded-lg flex font-medium items-center justify-center hover:(border-1 border-gray-200 bg-gray-100) text-gray-500 cursor-pointer"
class="h-9 rounded-lg flex font-medium items-center justify-center hover:(border-1 border-gray-200 bg-gray-100) text-gray-900 cursor-pointer"
@click="selectedDate = month"
>
{{ month.format('MMM') }}
@ -127,7 +127,7 @@ const compareYear = (date1: dayjs.Dayjs, date2: dayjs.Dayjs) => {
:class="{
'!bg-brand-50 !border-1 !border-brand-500': compareYear(year, selectedDate),
}"
class="h-9 rounded-lg flex font-medium items-center justify-center hover:(border-1 border-gray-200 bg-gray-100) text-gray-500 cursor-pointer"
class="h-9 rounded-lg flex font-medium items-center justify-center hover:(border-1 border-gray-200 bg-gray-100) text-gray-900 cursor-pointer"
@click="selectedDate = year"
>
{{ year.format('YYYY') }}

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

@ -269,33 +269,36 @@ const newRecord = () => {
emit('newRecord', { row, oldRow: {}, rowMeta: { new: true } })
}
const height = ref(0)
const width = ref(0)
const heightListener = () => {
height.value = window.innerHeight
const widthListener = () => {
width.value = window.innerWidth
}
onMounted(() => {
window.addEventListener('resize', heightListener)
window.addEventListener('resize', widthListener)
})
onUnmounted(() => {
window.removeEventListener('resize', heightListener)
window.removeEventListener('resize', widthListener)
})
</script>
<template>
<div
:class="{
'w-0': !props.visible,
'w-1/6 min-w-[22.1rem] nc-calendar-side-menu-open': props.visible,
'!w-0': !props.visible,
'min-w-[356px]': width > 1440 && props.visible,
'min-w-[264px]': width <= 1440 && props.visible,
'nc-calendar-side-menu-open': props.visible,
}"
class="h-full border-l-1 border-gray-200 transition-all"
data-testid="nc-calendar-side-menu"
>
<div
:class="{
'!hidden': height < 918,
'!hidden': width <= 1440,
'px-4 pt-3 pb-4 ': activeCalendarView === ('day' as const) || activeCalendarView === ('week' as const),
}"
class="flex flex-col"
>
@ -327,11 +330,11 @@ onUnmounted(() => {
<div
:class="{
'!border-t-0': height < 918,
'!border-t-0': width <= 1440,
}"
class="px-4 border-t-1 border-gray-200 relative flex flex-col gap-y-4 pt-3"
class="border-t-1 border-gray-200 relative flex flex-col gap-y-4 pt-3"
>
<div class="flex items-center gap-2">
<div class="flex px-4 items-center gap-2">
<a-input
v-model:value="searchQuery.value"
:class="{
@ -369,11 +372,12 @@ onUnmounted(() => {
v-if="calendarRange"
:ref="sideBarListRef"
:class="{
'!h-[calc(100vh-10.5rem)]': height < 918,
'h-[calc(100vh-36.2rem)]': activeCalendarView === ('day' as const) || activeCalendarView === ('week' as const) && height > 918,
'h-[calc(100vh-25.1rem)]': activeCalendarView === ('month' as const) || activeCalendarView === ('year' as const) && height > 918,
'!h-[calc(100vh-10.5rem)]': width <= 1440,
'h-[calc(100vh-36.2rem)]': activeCalendarView === ('day' as const) || activeCalendarView === ('week' as const) && width >= 1440,
'h-[calc(100vh-25.1rem)]': activeCalendarView === ('month' as const) || activeCalendarView === ('year' as const) && width >= 1440,
}"
class="gap-2 flex flex-col nc-scrollbar-md overflow-y-auto nc-calendar-top-height"
class="nc-scrollbar-md pl-4 pr-4 overflow-y-auto"
data-testid="nc-calendar-side-menu-list"
@scroll="sideBarListScrollHandle"
>
@ -397,56 +401,58 @@ onUnmounted(() => {
</div>
</div>
<template v-else-if="renderData.length > 0">
<LazySmartsheetRow v-for="(record, rowIndex) in renderData" :key="rowIndex" :row="record">
<LazySmartsheetCalendarSideRecordCard
:draggable="sideBarFilterOption === 'withoutDates' && activeCalendarView !== 'year'"
:from-date="
<div class="gap-2 flex flex-col">
<LazySmartsheetRow v-for="(record, rowIndex) in renderData" :key="rowIndex" :row="record">
<LazySmartsheetCalendarSideRecordCard
:draggable="sideBarFilterOption === 'withoutDates' && activeCalendarView !== 'year'"
:from-date="
record.rowMeta.range?.fk_from_col
? calDataType === UITypes.Date
? dayjs(record.row[record.rowMeta.range.fk_from_col.title!]).format('DD MMM')
: dayjs(record.row[record.rowMeta.range.fk_from_col.title!]).format('DD MMM•HH:mm A')
: null
"
:invalid="
:invalid="
record.rowMeta.range!.fk_to_col &&
dayjs(record.row[record.rowMeta.range!.fk_from_col.title!]).isAfter(
dayjs(record.row[record.rowMeta.range!.fk_to_col.title!]),
)
"
:row="record"
:to-date="
:row="record"
:to-date="
record.rowMeta.range!.fk_to_col
? calDataType === UITypes.Date
? dayjs(record.row[record.rowMeta.range!.fk_to_col.title!]).format('DD MMM')
: dayjs(record.row[record.rowMeta.range!.fk_to_col.title!]).format('DD MMM•HH:mm A')
: null
"
color="blue"
data-testid="nc-sidebar-record-card"
@click="emit('expand-record', record)"
@dragstart="dragStart($event, record)"
@dragover.prevent
>
<template v-if="!isRowEmpty(record, displayField)">
<div :class="{}">
<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>
</LazySmartsheetCalendarSideRecordCard>
</LazySmartsheetRow>
color="blue"
data-testid="nc-sidebar-record-card"
@click="emit('expand-record', record)"
@dragstart="dragStart($event, record)"
@dragover.prevent
>
<template v-if="!isRowEmpty(record, displayField)">
<div :class="{}">
<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>
</LazySmartsheetCalendarSideRecordCard>
</LazySmartsheetRow>
</div>
</template>
</div>
</div>

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

@ -17,7 +17,7 @@ const props = withDefaults(defineProps<Props>(), {
<template>
<div class="border-1 cursor-pointer border-gray-200 items-center px-2 py-3 rounded-lg">
<div class="flex items-center gap-2">
<div class="flex items-center pl-1 gap-2">
<span
:class="{
'bg-maroon-500': props.color === 'maroon',

49
packages/nc-gui/components/smartsheet/calendar/YearView.vue

@ -8,23 +8,44 @@ const months = computed(() => {
}
return months
})
const calendarContainer = ref<HTMLElement | null>(null)
const { width } = useWindowSize()
const size = ref('small')
const handleResize = () => {
if (width.value < 1608) {
size.value = 'small'
} else if (width.value < 2000) {
size.value = 'medium'
} else {
size.value = 'large'
}
}
onMounted(() => {
handleResize()
})
watch(width, handleResize)
</script>
<template>
<div
class="flex flex-wrap gap-6 pb-4 items-center justify-center overflow-auto nc-scrollbar-md"
data-testid="nc-calendar-year-view"
>
<NcDateWeekSelector
v-for="(_, index) in months"
:key="index"
v-model:active-dates="activeDates"
v-model:page-date="months[index]"
v-model:selected-date="selectedDate"
class="max-w-[350px]"
data-testid="nc-calendar-year-view-month-selector"
disable-pagination
/>
<div ref="calendarContainer" class="overflow-auto flex my-3 justify-center nc-scrollbar-md">
<div class="grid grid-cols-4 justify-items-center gap-3" data-testid="nc-calendar-year-view">
<NcDateWeekSelector
v-for="(_, index) in months"
:key="index"
v-model:active-dates="activeDates"
v-model:page-date="months[index]"
v-model:selected-date="selectedDate"
:size="size"
data-testid="nc-calendar-year-view-month-selector"
disable-pagination
/>
</div>
</div>
</template>

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

@ -193,7 +193,14 @@ const headerText = computed(() => {
</NcButton>
<template #overlay>
<div v-if="calendarRangeDropdown" class="min-w-[22.1rem]" @click.stop>
<div
v-if="calendarRangeDropdown"
:class="{
'px-4 pt-3 pb-4 ': activeCalendarView === 'week' || activeCalendarView === 'day',
}"
class="min-w-[22.1rem]"
@click.stop
>
<NcDateWeekSelector
v-if="activeCalendarView === ('day' as const)"
v-model:active-dates="activeDates"
@ -234,7 +241,6 @@ const headerText = computed(() => {
</NcButton>
</NcTooltip>
<NcButton
v-if="!isMobileMode"
v-e="`['c:calendar:calendar-${activeCalendarView}-today-btn']`"
data-testid="nc-calendar-today-btn"
size="small"

14
packages/nc-gui/composables/useCalendarViewStore.ts

@ -513,11 +513,23 @@ const [useProvideCalendarViewStore, useCalendarViewStore] = useInjectionState(
async function updateCalendarMeta(updateObj: Partial<CalendarType>) {
if (!viewMeta?.value?.id || !isUIAllowed('dataEdit') || isPublic.value) return
const updateValue = {
...(typeof calendarMetaData.value.meta === 'string'
? JSON.parse(calendarMetaData.value.meta)
: calendarMetaData.value.meta),
...(typeof updateObj.meta === 'string' ? JSON.parse(updateObj.meta) : updateObj.meta),
}
try {
await $api.dbView.calendarUpdate(viewMeta.value.id, updateObj)
await $api.dbView.calendarUpdate(viewMeta.value.id, {
...updateObj,
meta: JSON.stringify(updateValue),
})
calendarMetaData.value = {
...calendarMetaData.value,
...updateObj,
meta: updateValue,
}
} catch (e) {
message.error('Error updating changes')

2
packages/nc-gui/utils/browserUtils.ts

@ -19,6 +19,6 @@ export const getScrollbarWidth = () => {
outer.appendChild(inner)
const widthWithScroll = inner.offsetWidth
outer.parentNode.removeChild(outer)
outer?.parentNode?.removeChild(outer)
return widthNoScroll - widthWithScroll
}

Loading…
Cancel
Save