@ -213,7 +213,7 @@ const recordsAcrossAllRange = computed<{
}
} = { }
const perRecordHeight = 60
const perRecordHeight = 52
const columnArray : Array < Array < Row > > = [ [ ] ]
const gridTimeMap = new Map <
@ -263,15 +263,15 @@ const recordsAcrossAllRange = computed<{
scheduleEnd ,
} )
/ / T h e t o p o f t h e r e c o r d i s c a l c u l a t e d b a s e d o n t h e s t a r t h o u r a n d m i n u t e
const topInPixels = startDate . hour ( ) + startDate . minute ( )
const topInPixels = ( startDate . minute ( ) / 60 + startDate . hour ( ) ) * perRecordHeight
/ / A m i n i m u m h e i g h t o f 8 0 p x i s s e t f o r e a c h r e c o r d
/ / A m i n i m u m h e i g h t o f 5 2 p x i s s e t f o r e a c h r e c o r d
/ / T h e h e i g h t o f t h e r e c o r d i s c a l c u l a t e d b a s e d o n t h e d i f f e r e n c e b e t w e e n t h e s t a r t a n d e n d d a t e
const heightInPixels = Math . max ( endDate . diff ( startDate , 'minute' ) , perRecordHeight )
const style : Partial < CSSStyleDeclaration > = {
height : ` ${ heightInPixels } px ` ,
top : ` ${ topInPixels } px ` ,
height : ` ${ heightInPixels - 8 } px ` ,
top : ` ${ topInPixels + 4 } px ` ,
}
/ / T h i s p r o p e r t y i s u s e d t o d e t e r m i n e w h i c h s i d e t h e r e c o r d s h o u l d b e r o u n d e d . I t c a n b e t o p , b o t t o m , b o t h o r n o n e
@ -316,14 +316,14 @@ const recordsAcrossAllRange = computed<{
/ / T h e t o p o f t h e r e c o r d i s c a l c u l a t e d b a s e d o n t h e s t a r t h o u r
/ / U p d a t e s u c h t h a t i t i s a l s o b a s e d o n M i n u t e s
const topInPixels = startDate . minute ( ) + startDate . hour ( ) * 60
const topInPixels = ( startDate . minute ( ) / 60 + startDate . hour ( ) ) * perRecordHeight
/ / A m i n i m u m h e i g h t o f 8 0 p x i s s e t f o r e a c h r e c o r d
const heightInPixels = Math . max ( ( endDate . diff ( startDate , 'minute' ) / 60 ) * 60 , perRecordHeight )
const heightInPixels = Math . max ( ( endDate . diff ( startDate , 'minute' ) / 60 ) * 52 , perRecordHeight )
style = {
... style ,
top : ` ${ topInPixels + 1 } px ` ,
height : ` ${ heightInPixels - 2 } px ` ,
top : ` ${ topInPixels + 4 } px ` ,
height : ` ${ heightInPixels - 8 } px ` ,
}
recordsByRange . push ( {
@ -734,6 +734,94 @@ const dragStart = (event: MouseEvent, record: Row) => {
document . addEventListener ( 'mouseup' , onMouseUp )
}
/ / W e s u p p o r t d r a g a n d d r o p f r o m t h e s i d e b a r t o t h e d a y v i e w o f t h e d a t e f i e l d
const dropEvent = ( event : DragEvent ) => {
if ( ! isUIAllowed ( 'dataEdit' ) || ! container . value ) return
event . preventDefault ( )
const data = event . dataTransfer ? . getData ( 'text/plain' )
if ( data ) {
const {
record ,
isWithoutDates ,
} : {
record : Row
initialClickOffsetY : number
initialClickOffsetX : number
isWithoutDates : boolean
} = JSON . parse ( data )
const fromCol = record . rowMeta . range ? . fk _from _col
const toCol = record . rowMeta . range ? . fk _to _col
if ( ! fromCol ) return
const { top } = container . value . getBoundingClientRect ( )
const { scrollHeight } = container . value
/ / W e c a l c u l a t e t h e p e r c e n t a g e o f t h e m o u s e p o s i t i o n i n t h e s c r o l l c o n t a i n e r
const percentY = ( event . clientY - top + container . value . scrollTop ) / scrollHeight
const hour = Math . max ( Math . floor ( percentY * 23 ) , 0 )
const minutes = Math . min ( Math . max ( Math . round ( Math . floor ( ( percentY * 23 - hour ) * 60 ) / 15 ) * 15 , 0 ) , 60 )
const newStartDate = dayjs ( selectedDate . value ) . startOf ( 'day' ) . add ( hour , 'hour' ) . add ( minutes , 'minute' )
let endDate
const newRow = {
... record ,
row : {
... record . row ,
[ fromCol . title ! ] : dayjs ( newStartDate ) . format ( 'YYYY-MM-DD HH:mm:ssZ' ) ,
} ,
}
const updateProperty = [ fromCol . title ! ]
if ( toCol ) {
const fromDate = record . row [ fromCol . title ! ] ? dayjs ( record . row [ fromCol . title ! ] ) : null
const toDate = record . row [ toCol . title ! ] ? dayjs ( record . row [ toCol . title ! ] ) : null
if ( fromDate && toDate ) {
endDate = dayjs ( newStartDate ) . add ( toDate . diff ( fromDate , 'day' ) , 'day' )
} else if ( fromDate && ! toDate ) {
endDate = dayjs ( newStartDate ) . endOf ( 'day' )
} else if ( ! fromDate && toDate ) {
endDate = dayjs ( newStartDate ) . endOf ( 'day' )
} else {
endDate = newStartDate . clone ( )
}
newRow . row [ toCol . title ! ] = dayjs ( endDate ) . format ( 'YYYY-MM-DD HH:mm:ssZ' )
updateProperty . push ( toCol . title ! )
}
if ( ! newRow ) return
const newPk = extractPkFromRow ( newRow . row , meta . value ! . columns ! )
if ( dragElement . value && ! isWithoutDates ) {
formattedData . value = formattedData . value . map ( ( r ) => {
const pk = extractPkFromRow ( r . row , meta . value ! . columns ! )
return pk === newPk ? newRow : r
} )
} else {
formattedData . value = [ ... formattedData . value , newRow ]
if ( sideBarFilterOption . value !== 'allRecords' ) {
formattedSideBarData . value = formattedSideBarData . value . filter ( ( r ) => {
return extractPkFromRow ( r . row , meta . value ! . columns ! ) !== newPk
} )
}
}
if ( dragElement . value ) {
dragElement . value . style . boxShadow = 'none'
dragElement . value = null
}
updateRowProperty ( newRow , updateProperty , false )
}
}
const isOverflowAcrossHourRange = ( hour : dayjs . Dayjs ) => {
let startOfHour = hour . startOf ( 'hour' )
const endOfHour = hour . endOf ( 'hour' )
@ -802,47 +890,59 @@ watch(
< template >
< div
ref = "container"
class = "w-full relative no-selection h-[calc(100vh-10rem)] overflow-y-auto nc-scrollbar-md"
class = "w-full flex relative no-selection h-[calc(100vh-10rem)] overflow-y-auto nc-scrollbar-md"
data - testid = "nc-calendar-day-view"
@ drop = "dropEvent"
>
< div
v - for = "(hour, index) in hours"
: key = "index"
: class = " {
'!border-brand-500' : hour . isSame ( selectedTime ) ,
} "
class = "flex w-full h-15 nc-calendar-day-hour relative border-1 group hover:bg-gray-50 border-white border-b-gray-100"
data - testid = "nc-calendar-day-hour"
@ click = "selectHour(hour)"
@ dblclick = "newRecord(hour)"
>
< div class = "pt-2 px-4 text-xs text-gray-500 font-semibold h-15" >
{ { dayjs ( hour ) . format ( 'H A' ) } }
< div >
< div
v - for = "(hour, index) in hours"
: key = "index"
class = "flex h-13 relative border-1 group hover:bg-gray-50 border-white"
data - testid = "nc-calendar-day-hour"
@ click = "selectHour(hour)"
@ dblclick = "newRecord(hour)"
>
< div class = "w-16 border-b-0 pr-3 pl-2 text-right text-xs text-gray-400 font-semibold h-13" >
{ { dayjs ( hour ) . format ( 'hh a' ) } }
< / div >
< / div >
< div > < / div >
< NcDropdown
v - if = "calendarRange.length > 1"
< / div >
< div class = "w-full" >
< div
v - for = "(hour, index) in hours"
: key = "index"
: class = " {
'!block' : hour . isSame ( selectedTime ) ,
'!hidden' : ! hour . isSame ( selectedTime ) ,
'!border-brand-500' : hour . isSame ( selectedTime ) ,
} "
auto - close
class = "flex w-full border-l-gray-100 h-13 nc-calendar-day-hour relative border-1 group hover:bg-gray-50 border-white border-b-gray-100"
data - testid = "nc-calendar-day-hour"
@ click = "selectHour(hour)"
@ dblclick = "newRecord(hour)"
>
< NcButton
class = "!group-hover:block mr-4 my-auto ml-auto z-10 top-0 bottom-0 !group-hover:block absolute"
size = "xsmall"
type = "secondary"
< NcDropdown
v - if = "calendarRange.length > 1"
: class = " {
'!block' : hour . isSame ( selectedTime ) ,
'!hidden' : ! hour . isSame ( selectedTime ) ,
} "
auto - close
>
< component :is ="iconMap.plus" class = "h-4 w-4" / >
< / NcButton >
< template # overlay >
< NcMenu class = "w-64" >
< NcMenuItem > Select date field to add < / NcMenuItem >
< NcMenuItem
v - for = "(range, calIndex) in calendarRange"
: key = "calIndex"
class = "text-gray-800 font-semibold text-sm"
@ click = "
< NcButton
class = "!group-hover:block mr-4 my-auto ml-auto z-10 top-0 bottom-0 !group-hover:block absolute"
size = "xsmall"
type = "secondary"
>
< component :is ="iconMap.plus" class = "h-4 w-4" / >
< / NcButton >
< template # overlay >
< NcMenu class = "w-64" >
< NcMenuItem > Select date field to add < / NcMenuItem >
< NcMenuItem
v - for = "(range, calIndex) in calendarRange"
: key = "calIndex"
class = "text-gray-800 font-semibold text-sm"
@ click = "
( ) => {
let record = {
row : {
@ -860,25 +960,25 @@ watch(
emit ( 'newRecord' , record )
}
"
>
< div class = "flex items-center gap-1" >
< LazySmartsheetHeaderCellIcon :column-meta ="range.fk_from_col" / >
< span class = "ml-1" > { { range . fk _from _col ! . title ! } } < / span >
< / div >
< / NcMenuItem >
< / NcMenu >
< / template >
< / NcDropdown >
< NcButton
v - else
: class = " {
'!block' : hour . isSame ( selectedTime ) ,
'!hidden' : ! hour . isSame ( selectedTime ) ,
} "
class = "!group-hover:block mr-4 my-auto ml-auto z-10 top-0 bottom-0 !group-hover:block absolute"
size = "xsmall"
type = "secondary"
@ click = "
>
< div class = "flex items-center gap-1" >
< LazySmartsheetHeaderCellIcon :column-meta ="range.fk_from_col" / >
< span class = "ml-1" > { { range . fk _from _col ! . title ! } } < / span >
< / div >
< / NcMenuItem >
< / NcMenu >
< / template >
< / NcDropdown >
< NcButton
v - else
: class = " {
'!block' : hour . isSame ( selectedTime ) ,
'!hidden' : ! hour . isSame ( selectedTime ) ,
} "
class = "!group-hover:block mr-4 my-auto ml-auto z-10 top-0 bottom-0 !group-hover:block absolute"
size = "xsmall"
type = "secondary"
@ click = "
( ) => {
let record = {
row : {
@ -897,27 +997,28 @@ watch(
emit ( 'newRecord' , record )
}
"
>
< component :is ="iconMap.plus" class = "h-4 w-4" / >
< / NcButton >
< NcButton
v - if = "isOverflowAcrossHourRange(hour).isOverflow"
v - e = "`['c:calendar:week-view-more']`"
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" >
+
{ { isOverflowAcrossHourRange ( hour ) . overflowCount } }
more
< / span >
< / NcButton >
>
< component :is ="iconMap.plus" class = "h-4 w-4" / >
< / NcButton >
< NcButton
v - if = "isOverflowAcrossHourRange(hour).isOverflow"
v - e = "`['c:calendar:week-view-more']`"
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" >
+
{ { isOverflowAcrossHourRange ( hour ) . overflowCount } }
more
< / span >
< / NcButton >
< / div >
< / div >
< div class = "absolute inset-0 pointer-events-none" >
< div class = "relative !ml-[60px] " data -testid = " nc -calendar -day -record -container " >
< div class = "relative !ml-[68px] !mr-1 nc-calendar-day-record-container " data -testid = " nc -calendar -day -record -container " >
< template v-for ="(record, rowIndex) in recordsAcrossAllRange.record" :key ="rowIndex" >
< div
v - if = "record.rowMeta.style?.display !== 'none'"
@ -944,13 +1045,18 @@ watch(
< LazySmartsheetPlainCell
v - if = "!isRowEmpty(record, field!)"
v - model = "record.row[field!.title!]"
class = "text-xs"
class = "text-xs font-medium "
: bold = "getFieldStyle(field).bold"
: column = "field"
: italic = "getFieldStyle(field).italic"
: underline = "getFieldStyle(field).underline"
/ >
< / template >
< template # time >
< div class = "text-xs font-medium text-gray-400" >
{ { dayjs ( record . row [ record . rowMeta . range ? . fk _from _col ! . title ! ] ) . format ( 'h:mm a' ) } }
< / div >
< / template >
< / LazySmartsheetCalendarVRecordCard >
< / LazySmartsheetRow >
< / div >