UNPKG

vuetify

Version:

Vue Material Component Framework

240 lines (239 loc) 10.1 kB
import { createVNode as _createVNode, createTextVNode as _createTextVNode, createElementVNode as _createElementVNode, normalizeClass as _normalizeClass } from "vue"; // Styles import "./VTimePickerControls.css"; // Components import { VTimePickerField } from "./VTimePickerField.js"; import { VBtn } from "../VBtn/index.js"; // Composables import { useLocale } from "../../composables/locale.js"; import { useProxiedModel } from "../../composables/proxiedModel.js"; // Utilities import { ref, watch } from 'vue'; import { convert12to24, convert24to12, extractInteger, incrementHour, incrementMinuteOrSecond, pad } from "./util.js"; import { clamp, genericComponent, propsFactory, useRender } from "../../util/index.js"; // Types export const makeVTimePickerControlsProps = propsFactory({ ampm: Boolean, color: String, disabled: Boolean, hour: [Number, String], minute: [Number, String], second: [Number, String], period: String, readonly: Boolean, useSeconds: Boolean, value: Number, viewMode: String }, 'VTimePickerControls'); export const VTimePickerControls = genericComponent()({ name: 'VTimePickerControls', props: makeVTimePickerControlsProps(), emits: { 'update:period': data => true, 'update:viewMode': data => true, 'update:hour': v => true, 'update:minute': v => true, 'update:second': v => true }, setup(props, _ref) { let { emit, slots } = _ref; const { t } = useLocale(); const transformHours = { in: v => { if (v == null || isNaN(Number(v))) return null; const val = Number(v); return props.ampm ? pad(convert24to12(val)) : pad(val); }, out: v => { if (isNaN(Number(v)) || v == null || v === '') return null; const val = typeof v === 'string' ? extractInteger(v) : Number(v); if (val === null) return null; return props.ampm ? convert12to24(val, props.period ?? 'am') : clamp(val, 0, 23); } }; const hour = useProxiedModel(props, 'hour', undefined, transformHours.in, transformHours.out); const transformMinutesOrSeconds = { in: v => v != null && !isNaN(Number(v)) ? pad(`${v}`) : null, out: v => { if (isNaN(Number(v)) || v == null || v === '') return null; const val = typeof v === 'string' ? extractInteger(v) : Number(v); return val !== null ? clamp(val, 0, 59) : null; } }; const minute = useProxiedModel(props, 'minute', undefined, transformMinutesOrSeconds.in, transformMinutesOrSeconds.out); const second = useProxiedModel(props, 'second', undefined, transformMinutesOrSeconds.in, transformMinutesOrSeconds.out); function onHourFieldKeydown(e) { if (!['ArrowUp', 'ArrowDown'].includes(e.key)) return; e.preventDefault(); e.stopPropagation(); const current = Number(hour.value ?? 0); const period = props.ampm ? props.period ?? 'am' : null; const { value, togglePeriod } = incrementHour(current, e.key === 'ArrowUp', period); hour.value = pad(value); if (togglePeriod) { emit('update:period', props.period === 'am' ? 'pm' : 'am'); } } function onMinuteFieldKeydown(e) { if (!['ArrowUp', 'ArrowDown'].includes(e.key)) return; e.preventDefault(); e.stopPropagation(); minute.value = incrementMinuteOrSecond(Number(minute.value), e.key === 'ArrowUp'); } function onSecondFieldKeydown(e) { if (!['ArrowUp', 'ArrowDown'].includes(e.key)) return; e.preventDefault(); e.stopPropagation(); second.value = incrementMinuteOrSecond(Number(second.value), e.key === 'ArrowUp'); } function createInputInterceptor(valueTransformOut, compare, apply) { return e => { if (!e.data) return; const inputElement = e.target; const { value: existingTxt, selectionStart, selectionEnd } = inputElement ?? {}; if (extractInteger(e.data) === null) { e.preventDefault(); return; } const potentialNewInputVal = existingTxt ? existingTxt.slice(0, selectionStart) + e.data + existingTxt.slice(selectionEnd) : e.data; if (potentialNewInputVal.length > 2) { if (selectionStart === selectionEnd && selectionEnd === 0 && e.data.trim().startsWith('0')) { e.preventDefault(); inputElement.value = potentialNewInputVal.trim().substring(0, 2); apply(inputElement.value); if (e.data.trim().length === 1) { inputElement.setSelectionRange(1, 1); } return; } if (selectionStart === selectionEnd && selectionEnd === 1 && existingTxt.startsWith('0')) { e.preventDefault(); inputElement.value = potentialNewInputVal.trim().substring(0, 2); apply(inputElement.value); return; } const maxValue = props.viewMode === 'hour' ? props.ampm ? 12 : 23 : 59; const value = extractInteger(potentialNewInputVal); if (value > maxValue) { e.preventDefault(); inputElement.value = pad(String(extractInteger(e.data)).substring(0, 2)); apply(inputElement.value); return; } } const potentialNewNumber = valueTransformOut(potentialNewInputVal); if (compare(potentialNewNumber)) { // ignore input when results in the same number // prevents typing more digits e.preventDefault(); } }; } const hourInputRef = ref(); const minuteInputRef = ref(); const secondInputRef = ref(); watch(() => props.viewMode, (_, old) => { switch (old) { case 'hour': hourInputRef.value.blur(); break; case 'minute': minuteInputRef.value.blur(); break; case 'second': secondInputRef.value.blur(); break; } }); const hourInputFilter = createInputInterceptor(transformHours.out, v => transformHours.in(v) === hour.value, v => hour.value = v); const minuteInputFilter = createInputInterceptor(transformMinutesOrSeconds.out, v => transformMinutesOrSeconds.in(v) === minute.value, v => minute.value = v); const secondInputFilter = createInputInterceptor(transformMinutesOrSeconds.out, v => transformMinutesOrSeconds.in(v) === second.value, v => second.value = v); useRender(() => { return _createElementVNode("div", { "class": "v-time-picker-controls" }, [_createElementVNode("div", { "class": _normalizeClass({ 'v-time-picker-controls__time': true, 'v-time-picker-controls__time--with-ampm': props.ampm, 'v-time-picker-controls__time--with-seconds': props.useSeconds }) }, [_createVNode(VTimePickerField, { "ref": hourInputRef, "active": props.viewMode === 'hour', "color": props.color, "disabled": props.disabled, "label": t('$vuetify.timePicker.hour'), "modelValue": hour.value, "onUpdate:modelValue": v => hour.value = v, "onKeydown": onHourFieldKeydown, "onBeforeinput": hourInputFilter, "onFocus": () => emit('update:viewMode', 'hour') }, null), _createElementVNode("span", { "class": "v-time-picker-controls__time__separator" }, [_createTextVNode(":")]), _createVNode(VTimePickerField, { "ref": minuteInputRef, "active": props.viewMode === 'minute', "color": props.color, "disabled": props.disabled, "label": t('$vuetify.timePicker.minute'), "modelValue": minute.value, "onUpdate:modelValue": v => minute.value = v, "onKeydown": onMinuteFieldKeydown, "onBeforeinput": minuteInputFilter, "onFocus": () => emit('update:viewMode', 'minute') }, null), props.useSeconds && _createElementVNode("span", { "key": "secondsDivider", "class": "v-time-picker-controls__time__separator" }, [_createTextVNode(":")]), props.useSeconds && _createVNode(VTimePickerField, { "key": "secondsVal", "ref": secondInputRef, "active": props.viewMode === 'second', "color": props.color, "disabled": props.disabled, "label": t('$vuetify.timePicker.second'), "modelValue": second.value, "onUpdate:modelValue": v => second.value = v, "onKeydown": onSecondFieldKeydown, "onBeforeinput": secondInputFilter, "onFocus": () => emit('update:viewMode', 'second') }, null), props.ampm && _createElementVNode("div", { "class": "v-time-picker-controls__ampm" }, [_createVNode(VBtn, { "active": props.period === 'am', "color": props.period === 'am' ? props.color : undefined, "class": _normalizeClass({ 'v-time-picker-controls__ampm__am': true, 'v-time-picker-controls__ampm__btn': true, 'v-time-picker-controls__ampm__btn__active': props.period === 'am' }), "disabled": props.disabled, "text": t('$vuetify.timePicker.am'), "variant": props.disabled && props.period === 'am' ? 'elevated' : 'tonal', "onClick": () => props.period !== 'am' ? emit('update:period', 'am') : null }, null), _createVNode(VBtn, { "active": props.period === 'pm', "color": props.period === 'pm' ? props.color : undefined, "class": _normalizeClass({ 'v-time-picker-controls__ampm__pm': true, 'v-time-picker-controls__ampm__btn': true, 'v-time-picker-controls__ampm__btn__active': props.period === 'pm' }), "disabled": props.disabled, "text": t('$vuetify.timePicker.pm'), "variant": props.disabled && props.period === 'pm' ? 'elevated' : 'tonal', "onClick": () => props.period !== 'pm' ? emit('update:period', 'pm') : null }, null)])])]); }); return {}; } }); //# sourceMappingURL=VTimePickerControls.js.map