fix: enhance TimePicker to support HH:MM:SS input and truncate seconds

This commit is contained in:
Shariq Ansari 2025-08-28 18:58:22 +05:30
parent 223cbf4020
commit 4b4b188827

View File

@ -71,14 +71,14 @@ import { Popover, TextInput } from 'frappe-ui'
import { ref, computed, watch, nextTick } from 'vue' import { ref, computed, watch, nextTick } from 'vue'
const props = defineProps({ const props = defineProps({
modelValue: { type: String, default: '' }, // Expect 24h format HH:MM, but will parse flexible input modelValue: { type: String, default: '' }, // Expect 24h format HH:MM (seconds in HH:MM:SS accepted & truncated), parses flexible input
interval: { type: Number, default: 15 }, interval: { type: Number, default: 15 },
options: { options: {
// Optional complete override of generated options (array of { value: 'HH:MM', label?: string }) // Optional complete override of generated options (array of { value: 'HH:MM', label?: string })
type: Array, type: Array,
default: () => [], default: () => [],
}, },
placement: {type: String, default: 'bottom-start'}, placement: { type: String, default: 'bottom-start' },
placeholder: { type: String, default: 'Select time' }, placeholder: { type: String, default: 'Select time' },
variant: { type: String, default: 'outline' }, variant: { type: String, default: 'outline' },
allowCustom: { type: Boolean, default: true }, allowCustom: { type: Boolean, default: true },
@ -119,10 +119,10 @@ function optionId(idx) {
function minutesFromHHMM(str) { function minutesFromHHMM(str) {
if (!str) return null if (!str) return null
if (!/^\d{2}:\d{2}$/.test(str)) return null if (!/^\d{2}:\d{2}(:\d{2})?$/.test(str)) return null
const [h, m] = str.split(':').map(Number) const [h, m] = str.split(':').map((n) => parseInt(n))
if (h > 23 || m > 59) return null if (h > 23 || m > 59) return null
return h * 60 + m return h * 60 + m // ignore seconds if provided
} }
const minMinutes = computed(() => minutesFromHHMM(props.minTime)) const minMinutes = computed(() => minutesFromHHMM(props.minTime))
const maxMinutes = computed(() => minutesFromHHMM(props.maxTime)) const maxMinutes = computed(() => minutesFromHHMM(props.maxTime))
@ -168,6 +168,8 @@ function normalize24(raw) {
if (!raw) return '' if (!raw) return ''
// already HH:MM 24h // already HH:MM 24h
if (/^\d{2}:\d{2}$/.test(raw)) return raw if (/^\d{2}:\d{2}$/.test(raw)) return raw
// HH:MM:SS -> truncate seconds
if (/^\d{2}:\d{2}:\d{2}$/.test(raw)) return raw.slice(0, 5)
const parsed = parseFlexibleTime(raw) const parsed = parseFlexibleTime(raw)
return parsed.valid ? parsed.hh24 + ':' + parsed.mm : '' return parsed.valid ? parsed.hh24 + ':' + parsed.mm : ''
} }
@ -188,15 +190,23 @@ function parseFlexibleTime(input) {
s = s.replace(/\./g, '') // remove periods like a.m. s = s.replace(/\./g, '') // remove periods like a.m.
// Insert space before am/pm if missing // Insert space before am/pm if missing
s = s.replace(/(\d)(am|pm)$/, '$1 $2') s = s.replace(/(\d)(am|pm)$/, '$1 $2')
const re = /^(\d{1,2})(:?)(\d{0,2})\s*([ap]m)?$/ // supports: H, HH, HMM, HHMM, H:MM, HH:MM, HH:MM:SS, H:MM:SS + optional am/pm
// Simplify by using explicit colon format: H{1,2}(:MM)?(:SS)? with constraint that if SS present, MM must be present
const re = /^(\d{1,2})(?::(\d{1,2}))?(?::(\d{1,2}))?\s*([ap]m)?$/
const m = s.match(re) const m = s.match(re)
if (!m) return { valid: false } if (!m) return { valid: false }
let [, hhStr, colon, mmStr, ap] = m let [, hhStr, mmStr, ssStr, ap] = m
let hh = parseInt(hhStr) let hh = parseInt(hhStr)
if (isNaN(hh) || hh < 0 || hh > 23) return { valid: false } if (isNaN(hh) || hh < 0 || hh > 23) return { valid: false }
let mm = mmStr ? parseInt(mmStr) : 0 // If seconds provided but minutes missing -> invalid
if (colon && !mmStr) mm = 0 if (ssStr && !mmStr) return { valid: false }
let mm = mmStr != null && mmStr !== '' ? parseInt(mmStr) : 0
if (isNaN(mm) || mm < 0 || mm > 59) return { valid: false } if (isNaN(mm) || mm < 0 || mm > 59) return { valid: false }
if (ssStr) {
const ss = parseInt(ssStr)
if (isNaN(ss) || ss < 0 || ss > 59) return { valid: false }
// seconds currently ignored for internal value & total
}
if (ap) { if (ap) {
if (hh === 12 && ap === 'am') hh = 0 if (hh === 12 && ap === 'am') hh = 0
else if (hh < 12 && ap === 'pm') hh += 12 else if (hh < 12 && ap === 'pm') hh += 12
@ -357,7 +367,6 @@ function scheduleScroll() {
}) })
} }
watch(showOptions, (open) => { watch(showOptions, (open) => {
if (open) { if (open) {
emit('open') emit('open')