fix: make timepicker working with custom options
This commit is contained in:
parent
bff1b6156f
commit
eada826503
@ -17,6 +17,7 @@
|
|||||||
:value="timeValue"
|
:value="timeValue"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
@change="(e) => emitUpdate(e.target.value)"
|
@change="(e) => emitUpdate(e.target.value)"
|
||||||
|
@keydown.enter.prevent="(e) => emitUpdate(e.target.value)"
|
||||||
>
|
>
|
||||||
<template #prefix v-if="$slots.prefix">
|
<template #prefix v-if="$slots.prefix">
|
||||||
<slot name="prefix" />
|
<slot name="prefix" />
|
||||||
@ -29,13 +30,13 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div
|
<div
|
||||||
class="mt-2 min-w-40 max-h-72 overflow-hidden overflow-y-auto divide-y divide-outline-gray-modals rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none [&>div]:focus-visible:ring-0"
|
class="mt-2 min-w-40 max-h-72 overflow-hidden overflow-y-auto divide-y divide-outline-gray-modals rounded-lg bg-surface-modal shadow-2xl ring-1 ring-black ring-opacity-5"
|
||||||
:class="{
|
:class="{
|
||||||
'mt-2': ['bottom', 'left', 'right'].includes(placement),
|
'mt-2': ['bottom', 'left', 'right'].includes(placement),
|
||||||
'ml-2': placement == 'right-start',
|
'ml-2': placement == 'right-start',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<MenuItems class="p-1" ref="menu">
|
<MenuItems class="p-1 focus-visible:outline-none" ref="menu">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
v-for="option in options()"
|
v-for="option in options()"
|
||||||
:key="option.value"
|
:key="option.value"
|
||||||
@ -67,6 +68,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { TextInput } from 'frappe-ui'
|
import { TextInput } from 'frappe-ui'
|
||||||
import Dropdown from '@/components/frappe-ui/Dropdown.vue'
|
import Dropdown from '@/components/frappe-ui/Dropdown.vue'
|
||||||
|
import { allTimeSlots } from '@/components/Calendar/utils'
|
||||||
import { MenuItems, MenuItem } from '@headlessui/vue'
|
import { MenuItems, MenuItem } from '@headlessui/vue'
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
|
|
||||||
@ -91,6 +93,10 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: 'bottom',
|
default: 'bottom',
|
||||||
},
|
},
|
||||||
|
customOptions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
})
|
})
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
@ -107,34 +113,49 @@ const timeValue = computed(() => {
|
|||||||
time = time.substring(0, 5)
|
time = time.substring(0, 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeMap[time]) {
|
// Try to find a matching option (value is always in 24h format HH:MM)
|
||||||
time = timeMap[time]
|
const match = options().find((o) => o.value === time)
|
||||||
} else {
|
if (match) return match.label.split(' (')[0]
|
||||||
const [hour, minute] = time.split(':')
|
|
||||||
const ampm = hour >= 12 ? 'pm' : 'am'
|
|
||||||
const formattedHour = hour % 12 || 12
|
|
||||||
time = `${formattedHour}:${minute} ${ampm}`
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Fallback: format manually if the value isn't part of provided options
|
||||||
|
const [hourStr, minute] = time.split(':')
|
||||||
|
if (hourStr !== undefined && minute !== undefined) {
|
||||||
|
const hourNum = parseInt(hourStr)
|
||||||
|
if (!isNaN(hourNum)) {
|
||||||
|
const ampm = hourNum >= 12 ? 'pm' : 'am'
|
||||||
|
const formattedHour = hourNum % 12 || 12
|
||||||
|
return `${formattedHour}:${minute} ${ampm}`
|
||||||
|
}
|
||||||
|
}
|
||||||
return time
|
return time
|
||||||
})
|
})
|
||||||
|
|
||||||
const options = () => {
|
const options = () => {
|
||||||
let timeOptions = []
|
let timeOptions = []
|
||||||
for (const [key, value] of Object.entries(timeMap)) {
|
|
||||||
timeOptions.push({
|
const _options = props.customOptions.length
|
||||||
label: value,
|
? props.customOptions
|
||||||
value: key,
|
: allTimeSlots()
|
||||||
onClick: () => emitUpdate(key),
|
|
||||||
isSelected: () => {
|
for (const option of _options) {
|
||||||
let isSelected = isSelectedOrNearestOption()
|
timeOptions.push(timeObj(option.label, option.value))
|
||||||
return isSelected?.value === key && !isSelected?.isNearest
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return timeOptions
|
return timeOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function timeObj(label, value) {
|
||||||
|
return {
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
onClick: () => emitUpdate(value),
|
||||||
|
isSelected: () => {
|
||||||
|
let isSelected = isSelectedOrNearestOption()
|
||||||
|
return isSelected?.value === value && !isSelected?.isNearest
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const menu = ref(null)
|
const menu = ref(null)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@ -165,7 +186,7 @@ function convertTo24HourFormat(time) {
|
|||||||
function isSelectedOrNearestOption() {
|
function isSelectedOrNearestOption() {
|
||||||
const selectedTime = timeValue.value
|
const selectedTime = timeValue.value
|
||||||
const selectedOption = options().find(
|
const selectedOption = options().find(
|
||||||
(option) => option.label === selectedTime,
|
(option) => option.label.split(' (')[0] === selectedTime,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (selectedOption) {
|
if (selectedOption) {
|
||||||
@ -219,55 +240,4 @@ function updateScroll(el) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeMap = {
|
|
||||||
'00:00': '12:00 am',
|
|
||||||
'00:30': '12:30 am',
|
|
||||||
'01:00': '1:00 am',
|
|
||||||
'01:30': '1:30 am',
|
|
||||||
'02:00': '2:00 am',
|
|
||||||
'02:30': '2:30 am',
|
|
||||||
'03:00': '3:00 am',
|
|
||||||
'03:30': '3:30 am',
|
|
||||||
'04:00': '4:00 am',
|
|
||||||
'04:30': '4:30 am',
|
|
||||||
'05:00': '5:00 am',
|
|
||||||
'05:30': '5:30 am',
|
|
||||||
'06:00': '6:00 am',
|
|
||||||
'06:30': '6:30 am',
|
|
||||||
'07:00': '7:00 am',
|
|
||||||
'07:30': '7:30 am',
|
|
||||||
'08:00': '8:00 am',
|
|
||||||
'08:30': '8:30 am',
|
|
||||||
'09:00': '9:00 am',
|
|
||||||
'09:30': '9:30 am',
|
|
||||||
'10:00': '10:00 am',
|
|
||||||
'10:30': '10:30 am',
|
|
||||||
'11:00': '11:00 am',
|
|
||||||
'11:30': '11:30 am',
|
|
||||||
'12:00': '12:00 pm',
|
|
||||||
'12:30': '12:30 pm',
|
|
||||||
'13:00': '1:00 pm',
|
|
||||||
'13:30': '1:30 pm',
|
|
||||||
'14:00': '2:00 pm',
|
|
||||||
'14:30': '2:30 pm',
|
|
||||||
'15:00': '3:00 pm',
|
|
||||||
'15:30': '3:30 pm',
|
|
||||||
'16:00': '4:00 pm',
|
|
||||||
'16:30': '4:30 pm',
|
|
||||||
'17:00': '5:00 pm',
|
|
||||||
'17:30': '5:30 pm',
|
|
||||||
'18:00': '6:00 pm',
|
|
||||||
'18:30': '6:30 pm',
|
|
||||||
'19:00': '7:00 pm',
|
|
||||||
'19:30': '7:30 pm',
|
|
||||||
'20:00': '8:00 pm',
|
|
||||||
'20:30': '8:30 pm',
|
|
||||||
'21:00': '9:00 pm',
|
|
||||||
'21:30': '9:30 pm',
|
|
||||||
'22:00': '10:00 pm',
|
|
||||||
'22:30': '10:30 pm',
|
|
||||||
'23:00': '11:00 pm',
|
|
||||||
'23:30': '11:30 pm',
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
16
frontend/src/components/Calendar/utils.js
Normal file
16
frontend/src/components/Calendar/utils.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export function allTimeSlots() {
|
||||||
|
const out = []
|
||||||
|
for (let h = 0; h < 24; h++) {
|
||||||
|
for (const m of [0, 15, 30, 45]) {
|
||||||
|
const hh = String(h).padStart(2, '0')
|
||||||
|
const mm = String(m).padStart(2, '0')
|
||||||
|
const ampm = h >= 12 ? 'pm' : 'am'
|
||||||
|
const hour12 = h % 12 === 0 ? 12 : h % 12
|
||||||
|
out.push({
|
||||||
|
value: `${hh}:${mm}`,
|
||||||
|
label: `${hour12}:${mm} ${ampm}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user