Skip to content

Commit e67c411

Browse files
authored
fix(VDateInput): min/max with built-in adapter (#22295)
fixes #22291
1 parent 2e3c350 commit e67c411

File tree

4 files changed

+58
-33
lines changed

4 files changed

+58
-33
lines changed

packages/vuetify/dev/vuetify/date.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { StringDateAdapter } from '@/composables/date/adapters/string'
1010

1111
export default {
1212
// adapter: DateFnsAdapter,
13-
adapter: StringDateAdapter,
13+
// adapter: StringDateAdapter,
1414
formats: {
1515
// dayOfMonth: date => date.getDate(),
1616
},

packages/vuetify/src/components/VDatePicker/VDatePicker.tsx

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { VDefaultsProvider } from '@/components/VDefaultsProvider'
1212
import { makeVPickerProps, VPicker } from '@/labs/VPicker/VPicker'
1313

1414
// Composables
15+
import { useCalendarRange } from '@/composables/calendar'
1516
import { useDate } from '@/composables/date'
1617
import { daysDiff } from '@/composables/date/date'
1718
import { useLocale, useRtl } from '@/composables/locale'
@@ -124,27 +125,13 @@ export const VDatePicker = genericComponent<new <
124125
const viewMode = useProxiedModel(props, 'viewMode')
125126
// const inputMode = useProxiedModel(props, 'inputMode')
126127

127-
const minDate = computed(() => {
128-
const date = adapter.date(props.min)
129-
130-
return props.min && adapter.isValid(date) ? date : null
131-
})
132-
const maxDate = computed(() => {
133-
const date = adapter.date(props.max)
134-
135-
return props.max && adapter.isValid(date) ? date : null
136-
})
128+
const { minDate, maxDate, clampDate } = useCalendarRange(props)
137129

138130
const internal = computed(() => {
139131
const today = adapter.date()
140-
let value = today
141-
if (model.value?.[0]) {
142-
value = adapter.date(model.value[0])
143-
} else if (minDate.value && adapter.isBefore(today, minDate.value)) {
144-
value = minDate.value
145-
} else if (maxDate.value && adapter.isAfter(today, maxDate.value)) {
146-
value = maxDate.value
147-
}
132+
const value = model.value?.[0]
133+
? adapter.date(model.value[0])
134+
: clampDate(today)
148135

149136
return value && adapter.isValid(value) ? value : today
150137
})

packages/vuetify/src/composables/calendar.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,15 @@ export function useCalendar (props: CalendarProps) {
215215
})
216216
})
217217

218+
const { minDate, maxDate } = useCalendarRange(props)
219+
218220
function isDisabled (value: unknown) {
219221
if (props.disabled) return true
220222

221223
const date = adapter.date(value)
222224

223-
if (props.min && adapter.isBefore(adapter.endOfDay(date), adapter.date(props.min))) return true
224-
if (props.max && adapter.isAfter(date, adapter.date(props.max))) return true
225+
if (minDate.value && adapter.isBefore(adapter.endOfDay(date), minDate.value)) return true
226+
if (maxDate.value && adapter.isAfter(date, maxDate.value)) return true
225227

226228
if (Array.isArray(props.allowedDates) && props.allowedDates.length > 0) {
227229
return !props.allowedDates.some(d => adapter.isSameDay(adapter.date(d), date))
@@ -245,3 +247,41 @@ export function useCalendar (props: CalendarProps) {
245247
weekNumbers,
246248
}
247249
}
250+
251+
export function useCalendarRange (props: Pick<CalendarProps, 'min' | 'max'>) {
252+
const adapter = useDate()
253+
254+
const minDate = computed(() => {
255+
if (!props.min) return null
256+
const date = adapter.date(props.min)
257+
return adapter.isValid(date) ? date : null
258+
})
259+
260+
const maxDate = computed(() => {
261+
if (!props.max) return null
262+
const date = adapter.date(props.max)
263+
return adapter.isValid(date) ? date : null
264+
})
265+
266+
function clampDate (date: unknown) {
267+
if (minDate.value && adapter.isBefore(date, minDate.value)) {
268+
return minDate.value
269+
}
270+
if (maxDate.value && adapter.isAfter(date, maxDate.value)) {
271+
return maxDate.value
272+
}
273+
return date
274+
}
275+
276+
function isInAllowedRange (date: unknown) {
277+
return (!minDate.value || adapter.isAfter(date, minDate.value)) &&
278+
(!maxDate.value || adapter.isBefore(date, maxDate.value))
279+
}
280+
281+
return {
282+
minDate,
283+
maxDate,
284+
clampDate,
285+
isInAllowedRange,
286+
}
287+
}

packages/vuetify/src/labs/VDateInput/VDateInput.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { VMenu } from '@/components/VMenu/VMenu'
55
import { makeVTextFieldProps, VTextField } from '@/components/VTextField/VTextField'
66

77
// Composables
8+
import { useCalendarRange } from '@/composables/calendar'
89
import { useDate } from '@/composables/date'
910
import { createDateRange } from '@/composables/date/date'
1011
import { makeDateFormatProps, useDateFormat } from '@/composables/dateFormat'
@@ -110,15 +111,7 @@ export const VDateInput = genericComponent<new <
110111
const { isValid, parseDate, formatDate, parserFormat } = useDateFormat(props, currentLocale)
111112
const { mobile } = useDisplay(props)
112113

113-
const clamp = (date: unknown) => {
114-
if (props.max && adapter.isAfter(date, props.max)) {
115-
return props.max
116-
}
117-
if (props.min && adapter.isBefore(date, props.min)) {
118-
return props.min
119-
}
120-
return date
121-
}
114+
const { clampDate, isInAllowedRange } = useCalendarRange(props)
122115

123116
const emptyModelValue = () => props.multiple ? [] : null
124117

@@ -246,16 +239,21 @@ export const VDateInput = genericComponent<new <
246239
model.value = emptyModelValue()
247240
} else if (!props.multiple) {
248241
if (isValid(value)) {
249-
model.value = clamp(parseDate(value))
242+
model.value = clampDate(parseDate(value))
250243
}
251244
} else {
252245
const parts = value.trim().split(/\D+-\D+|[^\d\-/.]+/)
253246
if (parts.every(isValid)) {
254247
if (props.multiple === 'range') {
255-
const [start, stop] = parts.map(parseDate).map(clamp).toSorted((a, b) => adapter.isAfter(a, b) ? 1 : -1)
248+
const [start, stop] = parts
249+
.map(parseDate)
250+
.map(clampDate)
251+
.toSorted((a, b) => adapter.isAfter(a, b) ? 1 : -1)
256252
model.value = createDateRange(adapter, start, stop)
257253
} else {
258-
model.value = parts.map(parseDate).map(clamp)
254+
model.value = parts
255+
.map(parseDate)
256+
.filter(isInAllowedRange)
259257
}
260258
}
261259
}

0 commit comments

Comments
 (0)