8383 :second =" rangeTimes[0].second"
8484 :use-seconds =" useSeconds"
8585 :readonly =" readonly || formReadonly"
86- :disabled =" disabled || formDisabled || rangeTimeSelectDisabled "
86+ :disabled =" disabled || formDisabled || isRangeTimeSelectDisabled "
8787 :is-hour-allowed =" startAllowFns.isHourAllowed"
8888 :is-minute-allowed =" startAllowFns.isMinuteAllowed"
8989 :is-second-allowed =" startAllowFns.isSecondAllowed"
9696 :second =" rangeTimes[1].second"
9797 :use-seconds =" useSeconds"
9898 :readonly =" readonly || formReadonly"
99- :disabled =" disabled || formDisabled || rangeTimeSelectDisabled "
99+ :disabled =" disabled || formDisabled || isRangeTimeSelectDisabled "
100100 :is-hour-allowed =" endAllowFns.isHourAllowed"
101101 :is-minute-allowed =" endAllowFns.isMinuteAllowed"
102102 :is-second-allowed =" endAllowFns.isSecondAllowed"
@@ -206,9 +206,11 @@ export default defineComponent({
206206 const pickerMin = computed (() => getPickerBound (props .min ))
207207 const pickerMax = computed (() => getPickerBound (props .max ))
208208 const rangeSelecting = computed (() => Boolean (picker .value ?.rangeSelecting ))
209- const rangeTimeSelectDisabled = computed (
209+ const isRangeTimeSelectDisabled = computed (
210210 () => isDatetime .value && props .range && (rangeSelecting .value || getDayjsObjectsByModelValue ().length < 2 ),
211211 )
212+ const minDayjsObject = computed (() => (props .min ? dayjs (props .min ) : undefined ))
213+ const maxDayjsObject = computed (() => (props .max ? dayjs (props .max ) : undefined ))
212214 const singleTime = computed (() => getTimeParts (getDayjsObjectsByModelValue ()[0 ]))
213215 const rangeTimes = computed (() => {
214216 const dayjsObjects = getDayjsObjectsByModelValue ()
@@ -301,21 +303,21 @@ export default defineComponent({
301303 }
302304
303305 function clampDatetime(dayjsObject : Dayjs ) {
304- const minDayjsObject = props . min ? dayjs ( props . min ) : undefined
305- const maxDayjsObject = props . max ? dayjs ( props . max ) : undefined
306+ const min = minDayjsObject . value
307+ const max = maxDayjsObject . value
306308
307- if (minDayjsObject && dayjsObject .isBefore (minDayjsObject )) {
308- return minDayjsObject
309+ if (min && dayjsObject .isBefore (min )) {
310+ return min
309311 }
310312
311- if (maxDayjsObject && dayjsObject .isAfter (maxDayjsObject )) {
312- return maxDayjsObject
313+ if (max && dayjsObject .isAfter (max )) {
314+ return max
313315 }
314316
315317 return dayjsObject
316318 }
317319
318- function createTimeAllowFns (
320+ function createTimeSelectValidators (
319321 getDayjsObjectByModelValue : () => Dayjs | undefined ,
320322 getTimeParts : () => TimeParts ,
321323 position ? : DateInputRangePosition ,
@@ -333,14 +335,14 @@ export default defineComponent({
333335 .minute (unit === ' minute' ? value : time .minute )
334336 .second (unit === ' second' ? value : time .second )
335337
336- const minDayjsObject = props . min ? dayjs ( props . min ) : undefined
337- const maxDayjsObject = props . max ? dayjs ( props . max ) : undefined
338+ const min = minDayjsObject . value
339+ const max = maxDayjsObject . value
338340
339- if (minDayjsObject && timeOptionDayjsObject .endOf (unit ).isBefore (minDayjsObject )) {
341+ if (min && timeOptionDayjsObject .endOf (unit ).isBefore (min )) {
340342 return false
341343 }
342344
343- if (maxDayjsObject && timeOptionDayjsObject .startOf (unit ).isAfter (maxDayjsObject )) {
345+ if (max && timeOptionDayjsObject .startOf (unit ).isAfter (max )) {
344346 return false
345347 }
346348
@@ -364,16 +366,16 @@ export default defineComponent({
364366 }
365367 }
366368
367- const singleAllowFns = createTimeAllowFns (
369+ const singleAllowFns = createTimeSelectValidators (
368370 () => getDayjsObjectsByModelValue ()[0 ],
369371 () => singleTime .value ,
370372 )
371- const startAllowFns = createTimeAllowFns (
373+ const startAllowFns = createTimeSelectValidators (
372374 () => getDayjsObjectsByModelValue ()[0 ],
373375 () => rangeTimes .value [0 ],
374376 ' start' ,
375377 )
376- const endAllowFns = createTimeAllowFns (
378+ const endAllowFns = createTimeSelectValidators (
377379 () => getDayjsObjectsByModelValue ()[1 ],
378380 () => rangeTimes .value [1 ],
379381 ' end' ,
@@ -392,15 +394,15 @@ export default defineComponent({
392394 }
393395
394396 function isTimeCandidateAllowed(candidateDayjsObject : Dayjs , validators : DateInputAllowedTimeValidators ) {
395- const minDayjsObject = props . min ? dayjs ( props . min ) : undefined
396- const maxDayjsObject = props . max ? dayjs ( props . max ) : undefined
397+ const min = minDayjsObject . value
398+ const max = maxDayjsObject . value
397399 const hour = candidateDayjsObject .hour ()
398400 const minute = candidateDayjsObject .minute ()
399401 const second = candidateDayjsObject .second ()
400402
401403 return (
402- (! minDayjsObject || ! candidateDayjsObject .isBefore (minDayjsObject )) &&
403- (! maxDayjsObject || ! candidateDayjsObject .isAfter (maxDayjsObject )) &&
404+ (! min || ! candidateDayjsObject .isBefore (min )) &&
405+ (! max || ! candidateDayjsObject .isAfter (max )) &&
404406 (! validators .hours || validators .hours (hour )) &&
405407 (! validators .minutes || validators .minutes (minute , hour )) &&
406408 (! props .useSeconds || ! validators .seconds || validators .seconds (second , minute , hour )) &&
@@ -447,24 +449,33 @@ export default defineComponent({
447449 }
448450 }
449451
452+ function normalizeDateTimes(dayjsObjects : Dayjs []) {
453+ // Picker and TimeSelect are controlled selection paths, so range values are normalized to [start, end] here.
454+ // Manual input does not use this sorter. A reversed range remains invalid and is not committed.
455+ return props .range ? [... dayjsObjects ].sort ((a , b ) => a .valueOf () - b .valueOf ()) : dayjsObjects
456+ }
457+
450458 function commitDateTimes(dayjsObjects : Dayjs []) {
451- if (props .range ) {
452- const sorted = [... dayjsObjects ].sort ((a , b ) => a .valueOf () - b .valueOf ())
459+ const nextDayjsObjects = normalizeDateTimes (dayjsObjects )
453460
454- if (! isDayjsObjectSelectable (sorted [0 ], ' start' ) || ! isDayjsObjectSelectable (sorted [1 ], ' end' )) {
461+ if (props .range ) {
462+ if (
463+ ! isDayjsObjectSelectable (nextDayjsObjects [0 ], ' start' ) ||
464+ ! isDayjsObjectSelectable (nextDayjsObjects [1 ], ' end' )
465+ ) {
455466 return false
456467 }
457468
458- pickerValue .value = sorted .map ((dayjsObject ) => dayjsObject .format (getPickerFormat ()))
459- displayValue .value = sorted
469+ pickerValue .value = nextDayjsObjects .map ((dayjsObject ) => dayjsObject .format (getPickerFormat ()))
470+ displayValue .value = nextDayjsObjects
460471 .map ((dayjsObject ) => dayjsObject .format (getDisplayFormat ()))
461472 .join (props .rangeSeparator )
462- updateModelValue (sorted .map (dayjsObjectToModelValue ))
473+ updateModelValue (nextDayjsObjects .map (dayjsObjectToModelValue ))
463474
464475 return true
465476 }
466477
467- const [dayjsObject] = dayjsObjects
478+ const [dayjsObject] = nextDayjsObjects
468479
469480 if (! isDayjsObjectSelectable (dayjsObject )) {
470481 return false
@@ -540,19 +551,16 @@ export default defineComponent({
540551 }
541552
542553 function isDayjsObjectSelectable(dayjsObject : Dayjs , position ? : DateInputRangePosition ) {
543- const minDayjsObject = props . min ? dayjs ( props . min ) : undefined
544- const maxDayjsObject = props . max ? dayjs ( props . max ) : undefined
554+ const min = minDayjsObject . value
555+ const max = maxDayjsObject . value
545556 const unit =
546557 props .type === ' year' ? ' year' : props .type === ' month' ? ' month' : props .type === ' date' ? ' day' : undefined
547558
548- if (
549- minDayjsObject &&
550- (unit ? dayjsObject .isBefore (minDayjsObject , unit ) : dayjsObject .isBefore (minDayjsObject ))
551- ) {
559+ if (min && (unit ? dayjsObject .isBefore (min , unit ) : dayjsObject .isBefore (min ))) {
552560 return false
553561 }
554562
555- if (maxDayjsObject && (unit ? dayjsObject .isAfter (maxDayjsObject , unit ) : dayjsObject .isAfter (maxDayjsObject ))) {
563+ if (max && (unit ? dayjsObject .isAfter (max , unit ) : dayjsObject .isAfter (max ))) {
556564 return false
557565 }
558566
@@ -704,6 +712,7 @@ export default defineComponent({
704712
705713 const validDayjsObjects = dayjsObjects .filter (isTruthy )
706714
715+ // Manual input keeps the user-provided order. A reversed range is not auto-sorted or committed.
707716 if (props .range && validDayjsObjects [0 ].isAfter (validDayjsObjects [1 ])) {
708717 return
709718 }
@@ -741,6 +750,7 @@ export default defineComponent({
741750 return
742751 }
743752
753+ // The input path allows temporary invalid display text and commits only after parsing and validation pass.
744754 if (isMultipleOrRange .value ) {
745755 updateMultipleOrRangeStatesByDisplayValue ()
746756 } else {
@@ -755,70 +765,70 @@ export default defineComponent({
755765 return
756766 }
757767
768+ // On edit completion, restore from modelValue to clear uncommitted temporary input.
758769 updateDisplayValueByDayjsObjects (getDayjsObjectsByModelValue ())
759770 }
760771
761- function handlePickerChange( value : string | string []) {
762- if (isDisabled . value || isReadonly . value ) {
772+ function handleDatetimeRangePickerChange( dayjsObjects : Dayjs []) {
773+ if (dayjsObjects . length < 2 ) {
763774 return
764775 }
765776
766- pickerValue .value = value
767-
768- if (isArray (value )) {
769- const dayjsObjects = value
770- .map ((item ) => dayjs (item , getPickerFormat (), true ))
771- .filter ((dayjsObject ) => dayjsObject .isValid ())
777+ // DatePicker only provides dates. Datetime values must merge the current time before committing.
778+ const times = rangeTimes .value
779+ const resolved = dayjsObjects .map ((dayjsObject , index ) =>
780+ resolveSelectableDateTime (dayjsObject , times [index ] ?? getTimeParts (), index === 0 ? ' start' : ' end' ),
781+ )
772782
773- if (isDatetime .value && props .range ) {
774- if (dayjsObjects .length < 2 ) {
775- return
776- }
783+ if (resolved .every (isTruthy )) {
784+ if (! commitDateTimes (resolved .filter (isTruthy ))) {
785+ updateStatesByModelValue ()
786+ }
787+ } else {
788+ updateStatesByModelValue ()
789+ }
790+ }
777791
778- const times = rangeTimes . value
779- const resolved = dayjsObjects . map (( dayjsObject , index ) =>
780- resolveSelectableDateTime ( dayjsObject , times [ index ] ?? getTimeParts (), index === 0 ? ' start ' : ' end ' ),
781- )
792+ function handlePickerArrayChange( value : string []) {
793+ const dayjsObjects = value
794+ . map (( item ) => dayjs ( item , getPickerFormat (), true ))
795+ . filter (( dayjsObject ) => dayjsObject . isValid () )
782796
783- if (resolved .every (isTruthy )) {
784- if (! commitDateTimes (resolved .filter (isTruthy ))) {
785- updateStatesByModelValue ()
786- }
787- } else {
788- updateStatesByModelValue ()
789- }
797+ if (isDatetime .value && props .range ) {
798+ handleDatetimeRangePickerChange (dayjsObjects )
799+ return
800+ }
790801
791- return
792- }
802+ const modelValue = dayjsObjects .map (dayjsObjectToModelValue )
793803
794- const modelValue = dayjsObjects .map (dayjsObjectToModelValue )
804+ displayValue .value = dayjsObjects
805+ .map ((dayjsObject ) => dayjsObject .format (getDisplayFormat ()))
806+ .join (props .range ? props .rangeSeparator : props .separator )
795807
796- displayValue .value = dayjsObjects
797- .map ((dayjsObject ) => dayjsObject .format (getDisplayFormat ()))
798- .join (props .range ? props .rangeSeparator : props .separator )
808+ updateModelValue (modelValue )
809+ }
799810
800- updateModelValue (modelValue )
811+ function handleDatetimePickerChange(dayjsObject : Dayjs ) {
812+ const resolved = resolveSelectableDateTime (dayjsObject , singleTime .value )
801813
802- return
814+ if (resolved ) {
815+ if (! commitDateTimes ([resolved ])) {
816+ updateStatesByModelValue ()
817+ }
818+ } else {
819+ updateStatesByModelValue ()
803820 }
821+ }
804822
823+ function handlePickerSingleChange(value : string ) {
805824 const dayjsObject = dayjs (value , getPickerFormat (), true )
806825
807826 if (! dayjsObject .isValid ()) {
808827 return
809828 }
810829
811830 if (isDatetime .value ) {
812- const resolved = resolveSelectableDateTime (dayjsObject , singleTime .value )
813-
814- if (resolved ) {
815- if (! commitDateTimes ([resolved ])) {
816- updateStatesByModelValue ()
817- }
818- } else {
819- updateStatesByModelValue ()
820- }
821-
831+ handleDatetimePickerChange (dayjsObject )
822832 return
823833 }
824834
@@ -830,6 +840,21 @@ export default defineComponent({
830840 }
831841 }
832842
843+ function handlePickerChange(value : string | string []) {
844+ if (isDisabled .value || isReadonly .value ) {
845+ return
846+ }
847+
848+ pickerValue .value = value
849+
850+ if (isArray (value )) {
851+ handlePickerArrayChange (value )
852+ return
853+ }
854+
855+ handlePickerSingleChange (value )
856+ }
857+
833858 function getEmptyModelValue() {
834859 return isMultipleOrRange .value ? [] : undefined
835860 }
@@ -887,7 +912,7 @@ export default defineComponent({
887912 showMenu ,
888913 isFocusing ,
889914 rangeSelecting ,
890- rangeTimeSelectDisabled ,
915+ isRangeTimeSelectDisabled ,
891916 formDisabled ,
892917 formReadonly ,
893918 errorMessage ,
0 commit comments