add time formats

This commit is contained in:
Pete Matsyburka
2026-04-23 20:25:41 +03:00
parent 82500c2d7d
commit c7afe33c72
18 changed files with 384 additions and 144 deletions
+46 -26
View File
@@ -527,7 +527,8 @@ export default {
try {
return this.formatDate(
this.modelValue === '{{date}}' ? new Date() : new Date(this.modelValue),
this.field.preferences?.format || (this.locale.endsWith('-US') ? 'MM/DD/YYYY' : 'DD/MM/YYYY')
this.field.preferences?.format || (this.locale.endsWith('-US') ? 'MM/DD/YYYY' : 'DD/MM/YYYY'),
{ withTimePlaceholders: this.modelValue === '{{date}}' }
)
} catch {
return this.modelValue
@@ -646,36 +647,55 @@ export default {
return number
}
},
formatDate (date, format) {
const monthFormats = {
M: 'numeric',
MM: '2-digit',
MMM: 'short',
MMMM: 'long'
}
formatDate (date, format, { withTimePlaceholders = false } = {}) {
const monthFormats = { M: 'numeric', MM: '2-digit', MMM: 'short', MMMM: 'long' }
const dayFormats = { D: 'numeric', DD: '2-digit' }
const yearFormats = { YYYY: 'numeric', YYY: 'numeric', YY: '2-digit' }
const hourFormats = { H: 'numeric', HH: '2-digit', h: 'numeric', hh: '2-digit' }
const minuteFormats = { m: 'numeric', mm: '2-digit' }
const secondFormats = { s: 'numeric', ss: '2-digit' }
const dayFormats = {
D: 'numeric',
DD: '2-digit'
}
const hasTime = /[HhAasz]/.test(format)
const yearFormats = {
YYYY: 'numeric',
YYY: 'numeric',
YY: '2-digit'
}
const parts = new Intl.DateTimeFormat([], {
const opts = {
day: dayFormats[format.match(/D+/)],
month: monthFormats[format.match(/M+/)],
year: yearFormats[format.match(/Y+/)],
timeZone: 'UTC'
}).formatToParts(date)
year: yearFormats[format.match(/Y+/)]
}
return format
.replace(/D+/, parts.find((p) => p.type === 'day').value)
.replace(/M+/, parts.find((p) => p.type === 'month').value)
.replace(/Y+/, parts.find((p) => p.type === 'year').value)
if (format.match(/H+/)) { opts.hour = hourFormats[format.match(/H+/)[0]]; opts.hour12 = false }
if (format.match(/h+/)) { opts.hour = hourFormats[format.match(/h+/)[0]]; opts.hour12 = true }
if (/[Aa]/.test(format) && opts.hour12 === undefined) opts.hour12 = true
if (format.match(/m+/)) opts.minute = minuteFormats[format.match(/m+/)[0]]
if (format.match(/s+/)) opts.second = secondFormats[format.match(/s+/)[0]]
if (/z/.test(format)) opts.timeZoneName = 'short'
if (!hasTime) opts.timeZone = 'UTC'
const partTypes = {
M: 'month',
D: 'day',
Y: 'year',
H: 'hour',
h: 'hour',
m: 'minute',
s: 'second',
z: 'timeZoneName',
A: 'dayPeriod',
a: 'dayPeriod'
}
const parts = new Intl.DateTimeFormat([], opts).formatToParts(date)
return format.replace(/MMMM|MMM|MM|M|DD|D|YYYY|YYY|YY|HH|hh|H|h|mm|m|ss|s|A|a|z/g, (token) => {
if (withTimePlaceholders && /^(HH|hh|H|h|mm|m|ss|s|A|a)$/.test(token)) return '--'
const value = parts.find((p) => p.type === partTypes[token[0]])?.value
if (token === 'A') return (value || '').toUpperCase()
if (token === 'a') return (value || '').toLowerCase()
return value
})
},
updateMultipleSelectValue (value) {
if (this.modelValue?.includes(value)) {
+57 -16
View File
@@ -56,12 +56,18 @@
class="base-input !text-2xl text-center w-full"
:required="field.required"
:aria-describedby="field.description ? field.uuid + '-desc' : undefined"
type="date"
:name="`values[${field.uuid}]`"
:type="inputType"
:name="formatType === 'datetime' ? undefined : `values[${field.uuid}]`"
@keydown.enter="onEnter"
@focus="$emit('focus')"
@paste="onPaste"
>
<input
v-if="formatType === 'datetime'"
type="hidden"
:name="`values[${field.uuid}]`"
:value="modelValue"
>
</div>
</div>
</template>
@@ -97,14 +103,19 @@ export default {
},
emits: ['update:model-value', 'focus', 'submit'],
computed: {
formatType () {
const format = this.field.preferences?.format || ''
if (/[HhAasz]/.test(format)) return 'datetime'
if (format && !/[Dd]/.test(format)) return 'month'
return 'date'
},
inputType () {
return { datetime: 'datetime-local', month: 'month', date: 'date' }[this.formatType]
},
dateNowString () {
const today = new Date()
const yyyy = today.getFullYear()
const mm = String(today.getMonth() + 1).padStart(2, '0')
const dd = String(today.getDate()).padStart(2, '0')
return `${yyyy}-${mm}-${dd}`
return this.formatDateValue(new Date())
},
validationMin () {
if (this.field.validation?.min) {
@@ -121,6 +132,8 @@ export default {
}
},
withToday () {
if (this.formatType === 'datetime') return false
const todayDate = new Date().setHours(0, 0, 0, 0)
if (this.validationMin) {
@@ -137,9 +150,25 @@ export default {
},
value: {
set (value) {
if (this.formatType === 'datetime' && value) {
const d = new Date(value)
if (!isNaN(d)) {
this.$emit('update:model-value', d.toISOString())
return
}
}
this.$emit('update:model-value', value)
},
get () {
if (this.formatType === 'datetime') {
const d = new Date(this.modelValue)
return isNaN(d) ? '' : this.formatDateValue(d)
}
return this.modelValue
}
}
@@ -163,20 +192,32 @@ export default {
const parsedDate = new Date(pasteData)
if (!isNaN(parsedDate)) {
const inputEl = this.$refs.input
if (isNaN(parsedDate)) return
inputEl.valueAsDate = new Date(parsedDate.getTime() - parsedDate.getTimezoneOffset() * 60000)
inputEl.dispatchEvent(new Event('input', { bubbles: true }))
}
this.setInputValue(parsedDate)
},
setCurrentDate () {
this.setInputValue(new Date())
},
setInputValue (date) {
const inputEl = this.$refs.input
inputEl.valueAsDate = new Date(new Date().getTime() - new Date().getTimezoneOffset() * 60000)
if (this.formatType === 'date') {
inputEl.valueAsDate = new Date(date.getTime() - date.getTimezoneOffset() * 60000)
} else {
inputEl.value = this.formatDateValue(date)
}
inputEl.dispatchEvent(new Event('input', { bubbles: true }))
},
formatDateValue (date) {
const pad = (n) => String(n).padStart(2, '0')
const ymd = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
if (this.formatType === 'month') return ymd.slice(0, 7)
if (this.formatType === 'datetime') return `${ymd}T${pad(date.getHours())}:${pad(date.getMinutes())}`
return ymd
}
}
}