OWL
data_display
Time Picker
Odoo 19 OWL component — Time Picker (core)
Live preview
Interactive
Source excerpt
web/static/src/core/time_picker/time_picker.js
import { Component, onWillUpdateProps, useRef, useState } from "@odoo/owl";
import { Dropdown } from "@web/core/dropdown/dropdown";
import { useDropdownState } from "@web/core/dropdown/dropdown_hooks";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { getActiveHotkey } from "@web/core/hotkeys/hotkey_service";
import { Time, parseTime } from "@web/core/l10n/time";
import { mergeClasses } from "@web/core/utils/classname";
import { useChildRef } from "@web/core/utils/hooks";
const HOURS = [...Array(24)].map((_, i) => i);
const MINUTES = [...Array(60)].map((_, i) => i);
/**
* @typedef TimePickerProps
* @property {string} [class=""]
* @property {string|Time} [value]
* @property {(value: Time) => any} [onChange]
* @property {() => {}} [onInvalid]
* @property {boolean} [showSeconds=false]
* @property {number} [minutesRounding=5]
*/
export class TimePicker extends Component {
static template = "web.TimePicker";
static components = {
Dropdown,
DropdownItem,
};
static props = {
cssClass: { type: [String, Array, Object], optional: true },
inputCssClass: { type: [String, Array, Object], optional: true },
value: { type: [String, Time, { value: false }, { value: null }], optional: true },
onChange: { type: Function, optional: true },
onInvalid: { type: Function, optional: true },
showSeconds: { type: Boolean, optional: true },
minutesRounding: { type: Number, optional: true },
placeholder: { type: String, optional: true },
};
static defaultProps = {
cssClass: {},
inputCssClass: {},
value: "00:00",
onChange: () => {},
onInvalid: () => {},
showSeconds: false,
minutesRounding: 5,
};
setup() {
this.inputRef = useRef("inputRef");
this.menuRef = useChildRef();
this.dropdownState = useDropdownState();
this.state = useState({
value: null,
inputValue: "",
isValid: true,
});
/**@type {Time[]}*/
this.suggestions = [];
this.isNavigating = false;
this.navigationOptions = this.getNavigationOptions();
this.onPropsUpdated(this.props);
onWillUpdateProps((nextProps) => this.onPropsUpdated(nextProps));
}
get cssClass() {
return mergeClasses(this.props.cssClass, {
o_time_picker_seconds: this.props.showSeconds,
});
}
get inputCssClass() {
return mergeClasses(this.props.inputCssClass, {
o_invalid: !this.state.isValid,
});
}
/**
* @returns {import("@web/core/navigation/navigation").NavigationOptions}
*/
getNavigationOptions() {
const handleArrow = (navigator) => {
const value = this.suggestions[navigator.activeItemIndex];
if (value) {
this.state.inputValue = value.toString(this.props.showSeconds);
}
};
return {
virtualFocus: true,
onUpdated: (navigator) => (this.navigator = navigator),
hotkeys: {
enter: {
bypassEditableProtection: true,
callback: (navigator) => {
if (!this.isNavigating) {
const value = parseTime(this.inputRef.el.value, this.props.showSeconds);
if (value) {
this.setValue(value);
this.close();
}
} else if (navigator.activeItem) {
navigator.activeItem.select();
}
},
},
tab: {
bypassEditableProtection: true,
callback: (navigator) => {
if (navigator.activeItemIndex >= 0) {
this.setValue(this.suggestions[navigator.activeItemIndex]);
this.close();
}
},
},
arrowdown: {
callback: (navigator) => {
Registry / API
- Registry name
TimePicker- Category
—- Module
web- Slug
time-picker- Nav group
data_display