@ -7,14 +7,11 @@ import {
ColumnInj ,
ColumnInj ,
EditColumnInj ,
EditColumnInj ,
IsFormInj ,
IsFormInj ,
IsSurveyFormInj ,
ReadonlyInj ,
ReadonlyInj ,
inject ,
inject ,
isDrawerOrModalExist ,
parseProp ,
parseProp ,
ref ,
ref ,
useBase ,
useBase ,
useSelectedCellKeyupListener ,
watch ,
watch ,
} from '#imports'
} from '#imports'
@ -29,7 +26,7 @@ const emit = defineEmits(['update:modelValue'])
const { isMssql , isXcdbBase } = useBase ( )
const { isMssql , isXcdbBase } = useBase ( )
const { showNull } = useGlobal ( )
const { showNull , isMobileMode } = useGlobal ( )
const readOnly = inject ( ReadonlyInj , ref ( false ) )
const readOnly = inject ( ReadonlyInj , ref ( false ) )
@ -37,18 +34,22 @@ const active = inject(ActiveCellInj, ref(false))
const editable = inject ( EditModeInj , ref ( false ) )
const editable = inject ( EditModeInj , ref ( false ) )
const isForm = inject ( IsForm Inj , ref ( false ) )
const isGrid = inject ( IsGrid Inj , ref ( false ) )
const isSurvey Form = inject ( IsSurvey FormInj , ref ( false ) )
const isForm = inject ( IsFormInj , ref ( false ) )
const { t } = useI18n ( )
const { t } = useI18n ( )
const isEditColumn = inject ( EditColumnInj , ref ( false ) )
const isEditColumn = inject ( EditColumnInj , ref ( false ) )
const isExpandedForm = inject ( IsExpandedFormOpenInj , ref ( false ) )
const column = inject ( ColumnInj ) !
const column = inject ( ColumnInj ) !
const isDateInvalid = ref ( false )
const isDateInvalid = ref ( false )
const datePickerRef = ref < HTMLInputElement > ( )
const dateTimeFormat = computed ( ( ) => {
const dateTimeFormat = computed ( ( ) => {
const dateFormat = parseProp ( column ? . value ? . meta ) ? . date _format ? ? dateFormats [ 0 ]
const dateFormat = parseProp ( column ? . value ? . meta ) ? . date _format ? ? dateFormats [ 0 ]
const timeFormat = parseProp ( column ? . value ? . meta ) ? . time _format ? ? timeFormats [ 0 ]
const timeFormat = parseProp ( column ? . value ? . meta ) ? . time _format ? ? timeFormats [ 0 ]
@ -57,14 +58,13 @@ const dateTimeFormat = computed(() => {
let localModelValue = modelValue ? dayjs ( modelValue ) . utc ( ) . local ( ) : undefined
let localModelValue = modelValue ? dayjs ( modelValue ) . utc ( ) . local ( ) : undefined
const tempLocalValue = ref < dayjs .Dayjs > ( )
const isClearedInputMode = ref < boolean > ( false )
const open = ref ( false )
const localState = computed ( {
const localState = computed ( {
get ( ) {
get ( ) {
if ( ! modelValue && tempLocalValue . value ) {
if ( ! modelValue || isClearedInputMode . value ) {
return tempLocalValue . value
}
if ( ! modelValue ) {
return undefined
return undefined
}
}
@ -114,8 +114,10 @@ const localState = computed({
return dayjs ( modelValue ) . utc ( ) . local ( )
return dayjs ( modelValue ) . utc ( ) . local ( )
} ,
} ,
set ( val ? : dayjs . Dayjs ) {
set ( val ? : dayjs . Dayjs ) {
isClearedInputMode . value = false
if ( ! val ) {
if ( ! val ) {
emit ( 'update:modelValue' , null )
emit ( 'update:modelValue' , null )
return
return
}
}
@ -128,8 +130,6 @@ const localState = computed({
} ,
} ,
} )
} )
const open = ref ( false )
const isOpen = computed ( ( ) => {
const isOpen = computed ( ( ) => {
if ( readOnly . value ) return false
if ( readOnly . value ) return false
@ -137,27 +137,44 @@ const isOpen = computed(() => {
} )
} )
const randomClass = ` picker_ ${ Math . floor ( Math . random ( ) * 99999 ) } `
const randomClass = ` picker_ ${ Math . floor ( Math . random ( ) * 99999 ) } `
onClickOutside ( datePickerRef , ( e ) => {
if ( ( e . target as HTMLElement ) ? . closest ( ` . ${ randomClass } ` ) ) return
datePickerRef . value ? . blur ? . ( )
open . value = false
} )
const onBlur = ( e ) => {
if ( ( e ? . relatedTarget as HTMLElement ) ? . closest ( ` . ${ randomClass } ` ) ) return
open . value = false
}
watch (
watch (
open ,
open ,
( next ) => {
( next ) => {
if ( next ) {
if ( next ) {
onClickOutside ( document . querySelector ( ` . ${ randomClass } ` ) ! as HTMLDivElement , ( ) => ( open . value = false ) )
editable . value = true
datePickerRef . value ? . focus ? . ( )
if ( ! modelValue ) {
onClickOutside ( document . querySelector ( ` . ${ randomClass } ` ) ! as HTMLDivElement , ( e ) => {
tempLocalValue . value = dayjs ( new Date ( ) ) . utc ( ) . local ( )
if ( ( e ? . target as HTMLElement ) ? . closest ( ` .nc- ${ randomClass } ` ) ) {
} else {
return
tempLocalValue . value = undefined
}
}
open . value = false
} )
} else {
} else {
editable . value = false
isClearedInputMode . value = false
tempLocalValue . value = undefined
}
}
} ,
} ,
{ flush : 'post' } ,
{ flush : 'post' } ,
)
)
const placeholder = computed ( ( ) => {
const placeholder = computed ( ( ) => {
if ( isForm . value && ! isDateInvalid . value ) {
if (
( ( isForm . value || isExpandedForm . value ) && ! isDateInvalid . value ) ||
( isGrid . value && ! showNull . value && ! isDateInvalid . value && ! isSystemColumn ( column . value ) && active . value )
) {
return dateTimeFormat . value
return dateTimeFormat . value
} else if ( isEditColumn . value && ( modelValue === '' || modelValue === null ) ) {
} else if ( isEditColumn . value && ( modelValue === '' || modelValue === null ) ) {
return t ( 'labels.optional' )
return t ( 'labels.optional' )
@ -170,105 +187,18 @@ const placeholder = computed(() => {
}
}
} )
} )
useSelectedCellKeyupListener ( active , ( e : KeyboardEvent ) => {
switch ( e . key ) {
case 'Enter' :
e . stopPropagation ( )
/ / s k i p i f d r a w e r / m o d a l i s a c t i v e
if ( isDrawerOrModalExist ( ) ) {
return
}
if ( ! open . value ) {
/ / o p e n d a t e p i c k e r
open . value = true
} else {
/ / c l i c k O k b u t t o n t o s a v e t h e c u r r e n t l y s e l e c t e d d a t e
; ( document . querySelector ( '.nc-picker-datetime.active .ant-picker-ok button' ) as HTMLButtonElement ) ? . click ( )
}
break
case 'Escape' :
/ / s k i p i f d r a w e r / m o d a l i s a c t i v e
if ( isDrawerOrModalExist ( ) ) {
return
}
if ( open . value ) {
e . stopPropagation ( )
open . value = false
}
break
case 'ArrowLeft' :
if ( ! localState . value ) {
; ( document . querySelector ( '.nc-picker-datetime.active .ant-picker-header-prev-btn' ) as HTMLButtonElement ) ? . click ( )
} else {
const prevEl = document . querySelector ( '.nc-picker-datetime.active .ant-picker-cell-selected' )
? . previousElementSibling as HTMLButtonElement
if ( prevEl ) {
prevEl . click ( )
} else {
/ / g e t t h e l a s t t d f r o m p r e v i o u s t r
const prevRowLastEl = document
. querySelector ( '.nc-picker-datetime.active .ant-picker-cell-selected' )
? . closest ( 'tr' )
? . previousElementSibling ? . querySelector ( 'td:last-child' ) as HTMLButtonElement
if ( prevRowLastEl ) {
prevRowLastEl . click ( )
} else {
/ / g o t o t h e p r e v i o u s m o n t h
; ( document . querySelector ( '.nc-picker-datetime.active .ant-picker-header-prev-btn' ) as HTMLButtonElement ) ? . click ( )
}
}
}
break
case 'ArrowRight' :
if ( ! localState . value ) {
; ( document . querySelector ( '.nc-picker-datetime.active .ant-picker-header-next-btn' ) as HTMLButtonElement ) ? . click ( )
} else {
const nextEl = document . querySelector ( '.nc-picker-datetime.active .ant-picker-cell-selected' )
? . nextElementSibling as HTMLButtonElement
if ( nextEl ) {
nextEl . click ( )
} else {
/ / g e t t h e l a s t t d f r o m p r e v i o u s t r
const nextRowFirstEl = document
. querySelector ( '.nc-picker-datetime.active .ant-picker-cell-selected' )
? . closest ( 'tr' )
? . nextElementSibling ? . querySelector ( 'td:first-child' ) as HTMLButtonElement
if ( nextRowFirstEl ) {
nextRowFirstEl . click ( )
} else {
/ / g o t o t h e n e x t m o n t h
; ( document . querySelector ( '.nc-picker-datetime.active .ant-picker-header-next-btn' ) as HTMLButtonElement ) ? . click ( )
}
}
}
break
case 'ArrowUp' :
if ( ! localState . value )
( document . querySelector ( '.nc-picker-datetime.active .ant-picker-header-super-prev-btn' ) as HTMLButtonElement ) ? . click ( )
break
case 'ArrowDown' :
if ( ! localState . value )
( document . querySelector ( '.nc-picker-datetime.active .ant-picker-header-super-next-btn' ) as HTMLButtonElement ) ? . click ( )
break
case ';' :
localState . value = dayjs ( new Date ( ) )
break
}
} )
const cellClickHook = inject ( CellClickHookInj , null )
const cellClickHook = inject ( CellClickHookInj , null )
const cellClickHandler = ( ) => {
const cellClickHandler = ( ) => {
if ( readOnly . value ) return
if ( readOnly . value || open . value ) return
open . value = ( active . value || editable . value ) && ! open . value
open . value = active . value || editable . value
}
}
function okHandler ( val : dayjs . Dayjs | string ) {
function okHandler ( val : dayjs . Dayjs | string ) {
isClearedInputMode . value = false
if ( ! val ) {
if ( ! val ) {
emit ( 'update:modelValue' , null )
emit ( 'update:modelValue' , null )
return
} else if ( dayjs ( val ) . isValid ( ) ) {
}
if ( dayjs ( val ) . isValid ( ) ) {
/ / s e t t i n g l o c a l M o d e l V a l u e t o c a t e r N O W f u n c t i o n i n d a t e p i c k e r
/ / s e t t i n g l o c a l M o d e l V a l u e t o c a t e r N O W f u n c t i o n i n d a t e p i c k e r
localModelValue = dayjs ( val )
localModelValue = dayjs ( val )
/ / s e n d t h e p a y l o a d i n U T C f o r m a t
/ / s e n d t h e p a y l o a d i n U T C f o r m a t
@ -276,7 +206,12 @@ function okHandler(val: dayjs.Dayjs | string) {
}
}
open . value = ! open . value
open . value = ! open . value
if ( ! open . value && isGrid . value && ! isExpandedForm . value && ! isEditColumn . value ) {
datePickerRef . value ? . blur ? . ( )
editable . value = false
}
}
}
onMounted ( ( ) => {
onMounted ( ( ) => {
cellClickHook ? . on ( cellClickHandler )
cellClickHook ? . on ( cellClickHandler )
} )
} )
@ -284,7 +219,14 @@ onUnmounted(() => {
cellClickHook ? . on ( cellClickHandler )
cellClickHook ? . on ( cellClickHandler )
} )
} )
const clickHandler = ( ) => {
const clickHandler = ( e ) => {
if ( ( e . target as HTMLElement ) . closest ( ` .nc- ${ randomClass } .ant-picker-clear ` ) ) {
e . stopPropagation ( )
emit ( 'update:modelValue' , null )
open . value = false
return
}
if ( cellClickHook ) {
if ( cellClickHook ) {
return
return
}
}
@ -296,42 +238,99 @@ const isColDisabled = computed(() => {
} )
} )
const handleKeydown = ( e : KeyboardEvent ) => {
const handleKeydown = ( e : KeyboardEvent ) => {
if ( e . key !== 'Enter' ) {
e . stopPropagation ( )
}
switch ( e . key ) {
switch ( e . key ) {
case ' ' :
case 'Enter' :
if ( isSurveyForm . value ) {
if ( isOpen . value ) {
open . value = ! open . value
return okHandler ( ( e . target as HTMLInputElement ) . value )
} else {
open . value = true
}
}
break
return
case 'Escape' :
if ( open . value ) {
open . value = false
editable . value = false
if ( isGrid . value && ! isExpandedForm . value && ! isEditColumn . value ) {
datePickerRef . value ? . blur ? . ( )
}
} else {
editable . value = false
case 'Enter' :
datePickerRef . value ? . blur ? . ( )
if ( ! isSurveyForm . value ) {
}
open . value = ! open . value
return
case 'Tab' :
open . value = false
if ( isGrid . value ) {
editable . value = false
datePickerRef . value ? . blur ( )
}
return
default :
if ( ! open . value && /^[0-9a-z]$/i . test ( e . key ) ) {
open . value = true
}
}
break
}
}
}
}
useEventListener ( document , 'keydown' , ( e : KeyboardEvent ) => {
/ / T o p r e v e n t e v e n t l i s t e n e r o n n o n a c t i v e c e l l
if ( ! active . value ) return
if ( e . altKey || e . ctrlKey || e . shiftKey || e . metaKey || ! isGrid . value || isExpandedForm . value || isEditColumn . value ) return
switch ( e . key ) {
case ';' :
localState . value = dayjs ( new Date ( ) )
e . preventDefault ( )
break
default :
if ( ! isOpen . value && datePickerRef . value && /^[0-9a-z]$/i . test ( e . key ) ) {
isClearedInputMode . value = true
datePickerRef . value . focus ( )
editable . value = true
open . value = true
}
}
} )
watch ( editable , ( nextValue ) => {
if ( isGrid . value && nextValue && ! open . value ) {
open . value = true
}
} )
< / script >
< / script >
< template >
< template >
< a -date -picker
< a -date -picker
ref = "datePickerRef"
: value = "localState"
: value = "localState"
: disabled = "isColDisabled"
: disabled = "isColDisabled"
: show - time = "true"
: show - time = "true"
: bordered = "false"
: bordered = "false"
class = "nc-cell-field !w-full !py-1 !border-none !text-current"
class = "nc-cell-field nc-cell-picker-datetime !w-full !py-1 !border-none !text-current"
: class = "{ 'nc-null': modelValue === null && showNull }"
: class = "[`nc-${randomClass}`, { 'nc-null': modelValue === null && showNull }] "
: format = "dateTimeFormat"
: format = "dateTimeFormat"
: placeholder = "placeholder"
: placeholder = "placeholder"
: allow - clear = "!readOnly && !localState && !isPk"
: allow - clear = "!isColDisabled && !isEditColumn "
: input - read - only = "true"
: input - read - only = "!!isMobileMod e"
: dropdown - class - name = "`${randomClass} nc-picker-datetime children:border-1 children:border-gray-200 ${open ? 'active' : ''}`"
: dropdown - class - name = "`${randomClass} nc-picker-datetime children:border-1 children:border-gray-200 ${open ? 'active' : ''}`"
: open = "isOpen"
: open = "isOpen"
@ blur = "onBlur"
@ click = "clickHandler"
@ click = "clickHandler"
@ ok = "okHandler"
@ ok = "okHandler"
@ keydown = "handleKeydown"
@ keydown = "handleKeydown"
@ mouseup . stop
@ mousedown . stop
>
>
< template # suffixIcon > < / template >
< template # suffixIcon > < / template >
< / a - d a t e - p i c k e r >
< / a - d a t e - p i c k e r >
< div v-if ="!editable && isGrid" class="absolute inset-0 z-90 cursor-pointer" > < / div >
< / template >
< / template >
< style scoped >
< style scoped >