From f855877ce69fc84dada0450030823bfade55480a Mon Sep 17 00:00:00 2001 From: docmfried Date: Sun, 15 May 2016 11:06:18 +0200 Subject: [PATCH 1/7] Added new form field web timepicker widget for Odoo Version 9.0 --- web_widget_timepicker/README.rst | 50 + web_widget_timepicker/__init__.py | 22 + web_widget_timepicker/__openerp__.py | 51 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/src/css/jquery.timepicker.css | 72 + .../static/src/css/timepicker.css | 3 + .../static/src/js/jquery.timepicker.js | 1225 +++++++++++++++++ .../static/src/js/timepicker_widget.js | 89 ++ .../static/src/xml/time_picker.xml | 46 + web_widget_timepicker/views/assets.xml | 14 + 10 files changed, 1572 insertions(+) create mode 100644 web_widget_timepicker/README.rst create mode 100644 web_widget_timepicker/__init__.py create mode 100644 web_widget_timepicker/__openerp__.py create mode 100644 web_widget_timepicker/static/description/icon.png create mode 100644 web_widget_timepicker/static/src/css/jquery.timepicker.css create mode 100644 web_widget_timepicker/static/src/css/timepicker.css create mode 100644 web_widget_timepicker/static/src/js/jquery.timepicker.js create mode 100644 web_widget_timepicker/static/src/js/timepicker_widget.js create mode 100644 web_widget_timepicker/static/src/xml/time_picker.xml create mode 100644 web_widget_timepicker/views/assets.xml diff --git a/web_widget_timepicker/README.rst b/web_widget_timepicker/README.rst new file mode 100644 index 00000000..0e5cb8ba --- /dev/null +++ b/web_widget_timepicker/README.rst @@ -0,0 +1,50 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License: AGPL-3 + +============================== +Timepicker widget in form view +============================== + +This module defines a timepicker widget, to be used with either char fields +or (function) fields of type character. Use ``widget='timepicker'`` in your form view +definition. + +If you use the widget with a character field, the input field has the following default +timepicker options: + +* By default direct input is disabled +* By default the possible selection is based on 15 minute interval +* By default 24 hour mode with H:i format +* Scroll selection defaults to current server time + +The widget uses the jquery.timepicker plugin by Jon Thornton + + +Usage +===== + +This module defines a new widget type for form views input fileds. + +Set the attribute ``widget=timepicker`` in a ``field`` tag in a form view. + + +ToDo +==== + +Make timepicker options available in field defintion as additional attributes / options. + + +Credits +======= + +Jon Thornton (jquery.timepicker plugin) +jquery.timepicker plugin - This software is made available under the open source MIT License. © 2014 Jon Thornton and contributors + +Odoo Community Association (OCA) + + +Contributors +------------ + +* Michael Fried + diff --git a/web_widget_timepicker/__init__.py b/web_widget_timepicker/__init__.py new file mode 100644 index 00000000..96d91e91 --- /dev/null +++ b/web_widget_timepicker/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2015 BADEP (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + + diff --git a/web_widget_timepicker/__openerp__.py b/web_widget_timepicker/__openerp__.py new file mode 100644 index 00000000..faf3eeec --- /dev/null +++ b/web_widget_timepicker/__openerp__.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2016 Michael Fried @ Vividlab (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + + +{ + 'name': '', + 'version': '0.1', + 'author': 'Vividlab, Odoo Community Association (OCA)', + 'license': 'AGPL-3', + 'category': 'Web', + 'website': 'https://github.com/OCA/Web', + + # any module necessary for this one to work correctly + 'depends': [ + 'web' + ], + + 'css': [ 'static/src/css/jquery.timepicker.css', + 'static/src/css/timepicker.css', + ], + 'js': [ 'static/src/js/timepicker_widget.js', + 'static/src/js/jquery.timepicker.js', + ], + 'qweb' : [ 'static/src/xml/time_picker.xml', ], + + # always loaded + 'data': [ + 'views/assets.xml', + ], + + #Installation options + "installable": True, +} diff --git a/web_widget_timepicker/static/description/icon.png b/web_widget_timepicker/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/web_widget_timepicker/static/src/css/jquery.timepicker.css b/web_widget_timepicker/static/src/css/jquery.timepicker.css new file mode 100644 index 00000000..cd75f13f --- /dev/null +++ b/web_widget_timepicker/static/src/css/jquery.timepicker.css @@ -0,0 +1,72 @@ +.ui-timepicker-wrapper { + overflow-y: auto; + height: 150px; + width: 6.5em; + background: #fff; + border: 1px solid #ddd; + -webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2); + -moz-box-shadow:0 5px 10px rgba(0,0,0,0.2); + box-shadow:0 5px 10px rgba(0,0,0,0.2); + outline: none; + z-index: 10001; + margin: 0; +} + +.ui-timepicker-wrapper.ui-timepicker-with-duration { + width: 13em; +} + +.ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-30, +.ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-60 { + width: 11em; +} + +.ui-timepicker-list { + margin: 0; + padding: 0; + list-style: none; +} + +.ui-timepicker-duration { + margin-left: 5px; color: #888; +} + +.ui-timepicker-list:hover .ui-timepicker-duration { + color: #888; +} + +.ui-timepicker-list li { + padding: 3px 0 3px 5px; + cursor: pointer; + white-space: nowrap; + color: #000; + list-style: none; + margin: 0; +} + +.ui-timepicker-list:hover .ui-timepicker-selected { + background: #fff; color: #000; +} + +li.ui-timepicker-selected, +.ui-timepicker-list li:hover, +.ui-timepicker-list .ui-timepicker-selected:hover { + background: #1980EC; color: #fff; +} + +li.ui-timepicker-selected .ui-timepicker-duration, +.ui-timepicker-list li:hover .ui-timepicker-duration { + color: #ccc; +} + +.ui-timepicker-list li.ui-timepicker-disabled, +.ui-timepicker-list li.ui-timepicker-disabled:hover, +.ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled { + color: #888; + cursor: default; +} + +.ui-timepicker-list li.ui-timepicker-disabled:hover, +.ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled { + background: #f2f2f2; +} diff --git a/web_widget_timepicker/static/src/css/timepicker.css b/web_widget_timepicker/static/src/css/timepicker.css new file mode 100644 index 00000000..3bf9375a --- /dev/null +++ b/web_widget_timepicker/static/src/css/timepicker.css @@ -0,0 +1,3 @@ +.oe_form_editable .oe_form .oe_form_field_time input { + width: 7em; +} \ No newline at end of file diff --git a/web_widget_timepicker/static/src/js/jquery.timepicker.js b/web_widget_timepicker/static/src/js/jquery.timepicker.js new file mode 100644 index 00000000..d6cb947c --- /dev/null +++ b/web_widget_timepicker/static/src/js/jquery.timepicker.js @@ -0,0 +1,1225 @@ +/*! + * jquery-timepicker v1.10.1 - A jQuery timepicker plugin inspired by Google Calendar. It supports both mouse and keyboard navigation. + * Copyright (c) 2015 Jon Thornton - http://jonthornton.github.com/jquery-timepicker/ + * License: MIT + */ + + +(function (factory) { + if (typeof exports === "object" && exports && + typeof module === "object" && module && module.exports === exports) { + // Browserify. Attach to jQuery module. + factory(require("jquery")); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else { + // Browser globals + factory(jQuery); + } +}(function ($) { + var _ONE_DAY = 86400; + var _lang = { + am: 'am', + pm: 'pm', + AM: 'AM', + PM: 'PM', + decimal: '.', + mins: 'mins', + hr: 'hr', + hrs: 'hrs' + }; + + var methods = { + init: function(options) + { + return this.each(function() + { + var self = $(this); + + // pick up settings from data attributes + var attributeOptions = []; + for (var key in $.fn.timepicker.defaults) { + if (self.data(key)) { + attributeOptions[key] = self.data(key); + } + } + + var settings = $.extend({}, $.fn.timepicker.defaults, attributeOptions, options); + + if (settings.lang) { + _lang = $.extend(_lang, settings.lang); + } + + settings = _parseSettings(settings); + self.data('timepicker-settings', settings); + self.addClass('ui-timepicker-input'); + + if (settings.useSelect) { + _render(self); + } else { + self.prop('autocomplete', 'off'); + if (settings.showOn) { + for (var i in settings.showOn) { + self.on(settings.showOn[i]+'.timepicker', methods.show); + } + } + self.on('change.timepicker', _formatValue); + self.on('keydown.timepicker', _keydownhandler); + self.on('keyup.timepicker', _keyuphandler); + if (settings.disableTextInput) { + self.on('keydown.timepicker', _disableTextInputHandler); + } + + _formatValue.call(self.get(0)); + } + }); + }, + + show: function(e) + { + var self = $(this); + var settings = self.data('timepicker-settings'); + + if (e) { + e.preventDefault(); + } + + if (settings.useSelect) { + self.data('timepicker-list').focus(); + return; + } + + if (_hideKeyboard(self)) { + // block the keyboard on mobile devices + self.blur(); + } + + var list = self.data('timepicker-list'); + + // check if input is readonly + if (self.prop('readonly')) { + return; + } + + // check if list needs to be rendered + if (!list || list.length === 0 || typeof settings.durationTime === 'function') { + _render(self); + list = self.data('timepicker-list'); + } + + if (_isVisible(list)) { + return; + } + + self.data('ui-timepicker-value', self.val()); + _setSelected(self, list); + + // make sure other pickers are hidden + methods.hide(); + + // position the dropdown relative to the input + list.show(); + var listOffset = {}; + + if (settings.orientation.match(/r/)) { + // right-align the dropdown + listOffset.left = self.offset().left + self.outerWidth() - list.outerWidth() + parseInt(list.css('marginLeft').replace('px', ''), 10); + } else { + // left-align the dropdown + listOffset.left = self.offset().left + parseInt(list.css('marginLeft').replace('px', ''), 10); + } + + var verticalOrientation; + if (settings.orientation.match(/t/)) { + verticalOrientation = 't'; + } else if (settings.orientation.match(/b/)) { + verticalOrientation = 'b'; + } else if ((self.offset().top + self.outerHeight(true) + list.outerHeight()) > $(window).height() + $(window).scrollTop()) { + verticalOrientation = 't'; + } else { + verticalOrientation = 'b'; + } + + if (verticalOrientation == 't') { + // position the dropdown on top + list.addClass('ui-timepicker-positioned-top'); + listOffset.top = self.offset().top - list.outerHeight() + parseInt(list.css('marginTop').replace('px', ''), 10); + } else { + // put it under the input + list.removeClass('ui-timepicker-positioned-top'); + listOffset.top = self.offset().top + self.outerHeight() + parseInt(list.css('marginTop').replace('px', ''), 10); + } + + list.offset(listOffset); + + // position scrolling + var selected = list.find('.ui-timepicker-selected'); + + if (!selected.length) { + var timeInt = _time2int(_getTimeValue(self)); + if (timeInt !== null) { + selected = _findRow(self, list, timeInt); + } else if (settings.scrollDefault) { + selected = _findRow(self, list, settings.scrollDefault()); + } + } + + if (selected && selected.length) { + var topOffset = list.scrollTop() + selected.position().top - selected.outerHeight(); + list.scrollTop(topOffset); + } else { + list.scrollTop(0); + } + + // prevent scroll propagation + if(settings.stopScrollPropagation) { + $(document).on('wheel.ui-timepicker', '.ui-timepicker-wrapper', function(e){ + e.preventDefault(); + var currentScroll = $(this).scrollTop(); + $(this).scrollTop(currentScroll + e.originalEvent.deltaY); + }); + } + + // attach close handlers + $(document).on('touchstart.ui-timepicker mousedown.ui-timepicker', _closeHandler); + $(window).on('resize.ui-timepicker', _closeHandler); + if (settings.closeOnWindowScroll) { + $(document).on('scroll.ui-timepicker', _closeHandler); + } + + self.trigger('showTimepicker'); + + return this; + }, + + hide: function(e) + { + var self = $(this); + var settings = self.data('timepicker-settings'); + + if (settings && settings.useSelect) { + self.blur(); + } + + $('.ui-timepicker-wrapper').each(function() { + var list = $(this); + if (!_isVisible(list)) { + return; + } + + var self = list.data('timepicker-input'); + var settings = self.data('timepicker-settings'); + + if (settings && settings.selectOnBlur) { + _selectValue(self); + } + + list.hide(); + self.trigger('hideTimepicker'); + }); + + return this; + }, + + option: function(key, value) + { + if (typeof key == 'string' && typeof value == 'undefined') { + return $(this).data('timepicker-settings')[key]; + } + + return this.each(function(){ + var self = $(this); + var settings = self.data('timepicker-settings'); + var list = self.data('timepicker-list'); + + if (typeof key == 'object') { + settings = $.extend(settings, key); + } else if (typeof key == 'string') { + settings[key] = value; + } + + settings = _parseSettings(settings); + + self.data('timepicker-settings', settings); + + if (list) { + list.remove(); + self.data('timepicker-list', false); + } + + if (settings.useSelect) { + _render(self); + } + }); + }, + + getSecondsFromMidnight: function() + { + return _time2int(_getTimeValue(this)); + }, + + getTime: function(relative_date) + { + var self = this; + + var time_string = _getTimeValue(self); + if (!time_string) { + return null; + } + + var offset = _time2int(time_string); + if (offset === null) { + return null; + } + + if (!relative_date) { + relative_date = new Date(); + } + + // construct a Date from relative date, and offset's time + var time = new Date(relative_date); + time.setHours(offset / 3600); + time.setMinutes(offset % 3600 / 60); + time.setSeconds(offset % 60); + time.setMilliseconds(0); + + return time; + }, + + isVisible: function() { + var self = this; + var list = self.data('timepicker-list'); + return !!(list && _isVisible(list)); + }, + + setTime: function(value) + { + var self = this; + var settings = self.data('timepicker-settings'); + + if (settings.forceRoundTime) { + var prettyTime = _roundAndFormatTime(_time2int(value), settings) + } else { + var prettyTime = _int2time(_time2int(value), settings); + } + + if (value && prettyTime === null && settings.noneOption) { + prettyTime = value; + } + + _setTimeValue(self, prettyTime); + if (self.data('timepicker-list')) { + _setSelected(self, self.data('timepicker-list')); + } + + return this; + }, + + remove: function() + { + var self = this; + + // check if this element is a timepicker + if (!self.hasClass('ui-timepicker-input')) { + return; + } + + var settings = self.data('timepicker-settings'); + self.removeAttr('autocomplete', 'off'); + self.removeClass('ui-timepicker-input'); + self.removeData('timepicker-settings'); + self.off('.timepicker'); + + // timepicker-list won't be present unless the user has interacted with this timepicker + if (self.data('timepicker-list')) { + self.data('timepicker-list').remove(); + } + + if (settings.useSelect) { + self.show(); + } + + self.removeData('timepicker-list'); + + return this; + } + }; + + // private methods + + function _isVisible(elem) + { + var el = elem[0]; + return el.offsetWidth > 0 && el.offsetHeight > 0; + } + + function _parseSettings(settings) + { + if (settings.minTime) { + settings.minTime = _time2int(settings.minTime); + } + + if (settings.maxTime) { + settings.maxTime = _time2int(settings.maxTime); + } + + if (settings.durationTime && typeof settings.durationTime !== 'function') { + settings.durationTime = _time2int(settings.durationTime); + } + + if (settings.scrollDefault == 'now') { + settings.scrollDefault = function() { + return settings.roundingFunction(_time2int(new Date()), settings); + } + } else if (settings.scrollDefault && typeof settings.scrollDefault != 'function') { + var val = settings.scrollDefault; + settings.scrollDefault = function() { + return settings.roundingFunction(_time2int(val), settings); + } + } else if (settings.minTime) { + settings.scrollDefault = function() { + return settings.roundingFunction(settings.minTime, settings); + } + } + + if ($.type(settings.timeFormat) === "string" && settings.timeFormat.match(/[gh]/)) { + settings._twelveHourTime = true; + } + + if (settings.showOnFocus === false && settings.showOn.indexOf('focus') != -1) { + settings.showOn.splice(settings.showOn.indexOf('focus'), 1); + } + + if (settings.disableTimeRanges.length > 0) { + // convert string times to integers + for (var i in settings.disableTimeRanges) { + settings.disableTimeRanges[i] = [ + _time2int(settings.disableTimeRanges[i][0]), + _time2int(settings.disableTimeRanges[i][1]) + ]; + } + + // sort by starting time + settings.disableTimeRanges = settings.disableTimeRanges.sort(function(a, b){ + return a[0] - b[0]; + }); + + // merge any overlapping ranges + for (var i = settings.disableTimeRanges.length-1; i > 0; i--) { + if (settings.disableTimeRanges[i][0] <= settings.disableTimeRanges[i-1][1]) { + settings.disableTimeRanges[i-1] = [ + Math.min(settings.disableTimeRanges[i][0], settings.disableTimeRanges[i-1][0]), + Math.max(settings.disableTimeRanges[i][1], settings.disableTimeRanges[i-1][1]) + ]; + settings.disableTimeRanges.splice(i, 1); + } + } + } + + return settings; + } + + function _render(self) + { + var settings = self.data('timepicker-settings'); + var list = self.data('timepicker-list'); + + if (list && list.length) { + list.remove(); + self.data('timepicker-list', false); + } + + if (settings.useSelect) { + list = $(' + + + + + + + + + + \ No newline at end of file diff --git a/web_widget_timepicker/views/assets.xml b/web_widget_timepicker/views/assets.xml new file mode 100644 index 00000000..a1743b20 --- /dev/null +++ b/web_widget_timepicker/views/assets.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file From bc468c2506c9af6fb5f0f15f1ce9819360aa0b1d Mon Sep 17 00:00:00 2001 From: docmfried Date: Sun, 15 May 2016 11:32:24 +0200 Subject: [PATCH 2/7] [ADD] Added new form field web timepicker widget for Odoo Version 9.0 --- web_widget_timepicker/README.rst | 1 - .../static/src/css/timepicker.css | 2 +- .../static/src/xml/time_picker.xml | 27 +------------------ web_widget_timepicker/views/assets.xml | 2 +- 4 files changed, 3 insertions(+), 29 deletions(-) diff --git a/web_widget_timepicker/README.rst b/web_widget_timepicker/README.rst index 0e5cb8ba..5292b140 100644 --- a/web_widget_timepicker/README.rst +++ b/web_widget_timepicker/README.rst @@ -47,4 +47,3 @@ Contributors ------------ * Michael Fried - diff --git a/web_widget_timepicker/static/src/css/timepicker.css b/web_widget_timepicker/static/src/css/timepicker.css index 3bf9375a..1349e919 100644 --- a/web_widget_timepicker/static/src/css/timepicker.css +++ b/web_widget_timepicker/static/src/css/timepicker.css @@ -1,3 +1,3 @@ .oe_form_editable .oe_form .oe_form_field_time input { width: 7em; -} \ No newline at end of file +} diff --git a/web_widget_timepicker/static/src/xml/time_picker.xml b/web_widget_timepicker/static/src/xml/time_picker.xml index a4938059..94b5dac2 100644 --- a/web_widget_timepicker/static/src/xml/time_picker.xml +++ b/web_widget_timepicker/static/src/xml/time_picker.xml @@ -18,29 +18,4 @@ - - - \ No newline at end of file + diff --git a/web_widget_timepicker/views/assets.xml b/web_widget_timepicker/views/assets.xml index a1743b20..0f530683 100644 --- a/web_widget_timepicker/views/assets.xml +++ b/web_widget_timepicker/views/assets.xml @@ -11,4 +11,4 @@ - \ No newline at end of file + From 4d70b99df9585c4fcf7a18191dede9bb737a911b Mon Sep 17 00:00:00 2001 From: docmfried Date: Wed, 18 May 2016 17:44:21 +0200 Subject: [PATCH 3/7] [FIX]: Formal license and Travis LINT errors [FIX]: Renaming files according to OCA standards [FIX]: Change internal persistance form char to float [FIX]: Reorder jquery plugin to lib directory [ADD]: Form field record defintion can override timepicker plugin options --- web_widget_timepicker/README.rst | 24 ++++++--- web_widget_timepicker/__init__.py | 22 +------- web_widget_timepicker/__openerp__.py | 51 +++++++----------- ...mepicker.css => web_widget_timepicker.css} | 2 +- ...ker_widget.js => web_widget_timepicker.js} | 52 +++++++++++++++---- .../jquery.timepicker}/jquery.timepicker.css | 0 .../jquery.timepicker}/jquery.timepicker.js | 0 ...e_picker.xml => web_widget_timepicker.xml} | 3 +- ...s.xml => web_widget_timepicker_assets.xml} | 12 ++--- 9 files changed, 85 insertions(+), 81 deletions(-) rename web_widget_timepicker/static/src/css/{timepicker.css => web_widget_timepicker.css} (75%) rename web_widget_timepicker/static/src/js/{timepicker_widget.js => web_widget_timepicker.js} (55%) rename web_widget_timepicker/static/src/{css => lib/jquery.timepicker}/jquery.timepicker.css (100%) rename web_widget_timepicker/static/src/{js => lib/jquery.timepicker}/jquery.timepicker.js (100%) rename web_widget_timepicker/static/src/xml/{time_picker.xml => web_widget_timepicker.xml} (96%) rename web_widget_timepicker/views/{assets.xml => web_widget_timepicker_assets.xml} (51%) diff --git a/web_widget_timepicker/README.rst b/web_widget_timepicker/README.rst index 5292b140..3f6bf12c 100644 --- a/web_widget_timepicker/README.rst +++ b/web_widget_timepicker/README.rst @@ -5,19 +5,19 @@ Timepicker widget in form view ============================== -This module defines a timepicker widget, to be used with either char fields -or (function) fields of type character. Use ``widget='timepicker'`` in your form view -definition. +This module defines a timepicker widget, to be used with float fields +or (function) fields. Use ``widget='timepicker'`` in your form view +definition. It can be use as a replacement for the standard float_time widget. -If you use the widget with a character field, the input field has the following default +If you use the widget with a field record, the input field has the following default timepicker options: -* By default direct input is disabled +* By default direct user input is disabled * By default the possible selection is based on 15 minute interval * By default 24 hour mode with H:i format * Scroll selection defaults to current server time -The widget uses the jquery.timepicker plugin by Jon Thornton +The widget uses the jquery.timepicker plugin by Jon Thornton Usage @@ -27,17 +27,25 @@ This module defines a new widget type for form views input fileds. Set the attribute ``widget=timepicker`` in a ``field`` tag in a form view. +You can pass all options through the "timepicker" field in the options:: + + ... + + ... + +See the available options at https://github.com/jonthornton/jquery-timepicker#timepicker-plugin-for-jquery + ToDo ==== -Make timepicker options available in field defintion as additional attributes / options. +Sanity check on options available in field defintion as override options for timepicker widget. Credits ======= -Jon Thornton (jquery.timepicker plugin) +Jon Thornton (https://cdnjs.com/libraries/jquery-timepicker) jquery.timepicker plugin - This software is made available under the open source MIT License. © 2014 Jon Thornton and contributors Odoo Community Association (OCA) diff --git a/web_widget_timepicker/__init__.py b/web_widget_timepicker/__init__.py index 96d91e91..ea2c0d3a 100644 --- a/web_widget_timepicker/__init__.py +++ b/web_widget_timepicker/__init__.py @@ -1,22 +1,2 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2015 BADEP (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - - +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). \ No newline at end of file diff --git a/web_widget_timepicker/__openerp__.py b/web_widget_timepicker/__openerp__.py index faf3eeec..fb091f39 100644 --- a/web_widget_timepicker/__openerp__.py +++ b/web_widget_timepicker/__openerp__.py @@ -1,49 +1,34 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2016 Michael Fried @ Vividlab (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - +# © 2016 Michael Fried @ Vividlab () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': '', - 'version': '0.1', - 'author': 'Vividlab, Odoo Community Association (OCA)', + 'name': 'Web Timepicker Widget', + 'version': '9.0.1.0.0', + 'author': 'Michael Fried@Vividlab, Odoo Community Association (OCA)', 'license': 'AGPL-3', 'category': 'Web', 'website': 'https://github.com/OCA/Web', # any module necessary for this one to work correctly 'depends': [ - 'web' + 'web' + ], + 'css': [ + 'static/src/lib/jquery.timerpicker/jquery.timepicker.css', + 'static/src/css/web_widget_timepicker.css' + ], + 'js': [ + 'static/src/lib/jquery.timerpicker/jquery.timepicker.js', + 'static/src/js/web_widget_timepicker.js', + ], + 'qweb' : [ + 'static/src/xml/web_widget_timepicker.xml' ], - - 'css': [ 'static/src/css/jquery.timepicker.css', - 'static/src/css/timepicker.css', - ], - 'js': [ 'static/src/js/timepicker_widget.js', - 'static/src/js/jquery.timepicker.js', - ], - 'qweb' : [ 'static/src/xml/time_picker.xml', ], # always loaded 'data': [ - 'views/assets.xml', + 'views/web_widget_timepicker_assets.xml' ], #Installation options diff --git a/web_widget_timepicker/static/src/css/timepicker.css b/web_widget_timepicker/static/src/css/web_widget_timepicker.css similarity index 75% rename from web_widget_timepicker/static/src/css/timepicker.css rename to web_widget_timepicker/static/src/css/web_widget_timepicker.css index 1349e919..c66e9cd0 100644 --- a/web_widget_timepicker/static/src/css/timepicker.css +++ b/web_widget_timepicker/static/src/css/web_widget_timepicker.css @@ -1,3 +1,3 @@ .oe_form_editable .oe_form .oe_form_field_time input { - width: 7em; + width: 6em; } diff --git a/web_widget_timepicker/static/src/js/timepicker_widget.js b/web_widget_timepicker/static/src/js/web_widget_timepicker.js similarity index 55% rename from web_widget_timepicker/static/src/js/timepicker_widget.js rename to web_widget_timepicker/static/src/js/web_widget_timepicker.js index 74cc7820..f68c0369 100644 --- a/web_widget_timepicker/static/src/js/timepicker_widget.js +++ b/web_widget_timepicker/static/src/js/web_widget_timepicker.js @@ -1,21 +1,22 @@ -odoo.define('timepicker.form_widgets', function (require) { - +odoo.define('web_widget_timepicker.form_widgets', function (require) { "use strict"; var core = require('web.core'); var formats = require('web.formats'); var common = require('web.form_common'); - var _t = core._t; - var TimePicker = common.AbstractField.extend(common.ReinitializeFieldMixin, { + is_field_number: true, template: "TimePickerField", + internal_format: 'float_time', widget_class: 'oe_form_field_time', events: { 'change input': 'store_dom_value', }, init: function (field_manager, node) { this._super(field_manager, node); + + this.internal_set_value(0); this.options = _.defaults( {}, { disableTextInput: true, @@ -29,14 +30,45 @@ odoo.define('timepicker.form_widgets', function (require) { initialize_content: function() { if(!this.get("effective_readonly")) { this.$input = this.$el.find('input'); - this.$input.timepicker(this.options); + + var effective_options = this.options; + + if(typeof this.node.attrs.options !== 'undefined' && this.node.attrs.options.length > 0 ) { + + var custom_options = eval('('+ this.node.attrs.options +')') + + // for(var key in custom_options) { + // console.log('attr key : ' + key); + // console.log('attr value : ' + custom_options[key] ); + // } + + // if(typeof effective_options === 'object') { + // for(var key in effective_options) { + // console.log('def key : ' + key); + // console.log('def value : ' + effective_options[key] ); + // } + // } + + if(typeof custom_options === 'object') { + effective_options = $.extend({}, this.options, custom_options ); + } + + // if(typeof effective_options === 'object') { + // for(var key in effective_options) { + // console.log('merge key : ' + key); + // console.log('merge value : ' + effective_options[key] ); + // } + // } + } + + this.$input.timepicker(effective_options); this.setupFocus(this.$('input')); } }, is_syntax_valid: function() { if (!this.get("effective_readonly") && this.$("input").size() > 0) { try { - this.parse_value(this.$('input').val(), ''); + this.parse_value(this.$('input').val(),''); return true; } catch(e) { return false; @@ -62,17 +94,17 @@ odoo.define('timepicker.form_widgets', function (require) { if (!this.get('effective_readonly')) { this.internal_set_value( this.parse_value( - this.$('input').val())); + this.$('input').val(),'')); } }, parse_value: function(val, def) { - return formats.parse_value(val, this, def); + return formats.parse_value(val, {"widget": this.internal_format}, def); }, format_value: function(val, def) { - return formats.format_value(val, this, def); + return formats.format_value(val, {"widget": this.internal_format}, def); }, render_value: function() { - var show_value = this.format_value(this.get('value'), ''); + var show_value = this.format_value(this.get('value'),''); if (!this.get("effective_readonly")) { this.$input = this.$el.find('input'); diff --git a/web_widget_timepicker/static/src/css/jquery.timepicker.css b/web_widget_timepicker/static/src/lib/jquery.timepicker/jquery.timepicker.css similarity index 100% rename from web_widget_timepicker/static/src/css/jquery.timepicker.css rename to web_widget_timepicker/static/src/lib/jquery.timepicker/jquery.timepicker.css diff --git a/web_widget_timepicker/static/src/js/jquery.timepicker.js b/web_widget_timepicker/static/src/lib/jquery.timepicker/jquery.timepicker.js similarity index 100% rename from web_widget_timepicker/static/src/js/jquery.timepicker.js rename to web_widget_timepicker/static/src/lib/jquery.timepicker/jquery.timepicker.js diff --git a/web_widget_timepicker/static/src/xml/time_picker.xml b/web_widget_timepicker/static/src/xml/web_widget_timepicker.xml similarity index 96% rename from web_widget_timepicker/static/src/xml/time_picker.xml rename to web_widget_timepicker/static/src/xml/web_widget_timepicker.xml index 94b5dac2..2f512c18 100644 --- a/web_widget_timepicker/static/src/xml/time_picker.xml +++ b/web_widget_timepicker/static/src/xml/web_widget_timepicker.xml @@ -1,5 +1,4 @@ - @@ -18,4 +17,4 @@ - + \ No newline at end of file diff --git a/web_widget_timepicker/views/assets.xml b/web_widget_timepicker/views/web_widget_timepicker_assets.xml similarity index 51% rename from web_widget_timepicker/views/assets.xml rename to web_widget_timepicker/views/web_widget_timepicker_assets.xml index 0f530683..5dd66272 100644 --- a/web_widget_timepicker/views/assets.xml +++ b/web_widget_timepicker/views/web_widget_timepicker_assets.xml @@ -2,13 +2,13 @@ - - \ No newline at end of file + From 010245c31b5a77fdfb474ce0379f10872e79b24d Mon Sep 17 00:00:00 2001 From: docmfried Date: Wed, 18 May 2016 22:55:09 +0200 Subject: [PATCH 5/7] [FIX]: More TravisCI LINT errors [FIX]: Replace eval() function in web_widget_timepicker.js [FIX]: Change options to data-options in field record defintion --- web_widget_timepicker/README.rst | 2 +- web_widget_timepicker/__init__.py | 2 +- .../static/src/js/web_widget_timepicker.js | 48 ++++++++----------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/web_widget_timepicker/README.rst b/web_widget_timepicker/README.rst index 3f6bf12c..fa64c2df 100644 --- a/web_widget_timepicker/README.rst +++ b/web_widget_timepicker/README.rst @@ -30,7 +30,7 @@ Set the attribute ``widget=timepicker`` in a ``field`` tag in a form view. You can pass all options through the "timepicker" field in the options:: ... - + ... See the available options at https://github.com/jonthornton/jquery-timepicker#timepicker-plugin-for-jquery diff --git a/web_widget_timepicker/__init__.py b/web_widget_timepicker/__init__.py index ea2c0d3a..2977e4d6 100644 --- a/web_widget_timepicker/__init__.py +++ b/web_widget_timepicker/__init__.py @@ -1,2 +1,2 @@ # -*- coding: utf-8 -*- -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). \ No newline at end of file +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/web_widget_timepicker/static/src/js/web_widget_timepicker.js b/web_widget_timepicker/static/src/js/web_widget_timepicker.js index 3701882b..4db68f30 100644 --- a/web_widget_timepicker/static/src/js/web_widget_timepicker.js +++ b/web_widget_timepicker/static/src/js/web_widget_timepicker.js @@ -5,6 +5,15 @@ odoo.define('web_widget_timepicker.form_widgets', function (require) { var formats = require('web.formats'); var common = require('web.form_common'); + // Snippet from http://stackoverflow.com/questions/9036429/convert-object-string-to-json + function cleanup_str2json(str) { + return (str.length > 0 && str !== undefined) ? str + // wrap keys without quote with valid double quote + .replace(/([\$\w]+)\s*:/g, function(_, $1){return '"'+$1+'":'}) + // replacing single quote wrapped ones to double quote + .replace(/'([^']+)'/g, function(_, $1){return '"'+$1+'"'}) : undefined; + }; + var TimePicker = common.AbstractField.extend(common.ReinitializeFieldMixin, { is_field_number: true, template: "TimePickerField", @@ -29,39 +38,24 @@ odoo.define('web_widget_timepicker.form_widgets', function (require) { }, initialize_content: function() { if(!this.get("effective_readonly")) { - this.$input = this.$el.find('input'); - - var effective_options = this.options; + var custom_options; - if(typeof this.node.attrs.options !== 'undefined' && this.node.attrs.options.length > 0 ) { + if( this.node.attrs['data-options'] !== undefined && this.node.attrs['data-options'].length > 0) { + // First try to use jquery data function to create object + custom_options = $(this.node).data('options'); - var custom_options = eval('('+ this.node.attrs.options +')'); - - // for(var key in custom_options) { - // console.log('attr key : ' + key); - // console.log('attr value : ' + custom_options[key] ); - // } - - // if(typeof effective_options === 'object') { - // for(var key in effective_options) { - // console.log('def key : ' + key); - // console.log('def value : ' + effective_options[key] ); - // } - // } - - if(typeof custom_options === 'object') { - effective_options = $.extend({}, this.options, custom_options ); + if(typeof custom_options !== 'object') { + // No garantee that the input data-options string is valid JSON format so try to cleanup + custom_options = JSON.parse(cleanup_str2json(this.node.attrs['data-options'])); } + } - // if(typeof effective_options === 'object') { - // for(var key in effective_options) { - // console.log('merge key : ' + key); - // console.log('merge value : ' + effective_options[key] ); - // } - // } + if(typeof custom_options === 'object') { + this.$el.find('input').timepicker($.extend({}, this.options, custom_options )); + } else { + this.$el.find('input').timepicker(this.options); } - this.$input.timepicker(effective_options); this.setupFocus(this.$('input')); } }, From 720a5eb08dd8624312971066642723a8d5d9437f Mon Sep 17 00:00:00 2001 From: docmfried Date: Thu, 19 May 2016 01:20:04 +0200 Subject: [PATCH 6/7] [FIX]: Remove needless comments in __openerp__.py [FIX]: Cleanup user provided timepicker options initialization and handling --- web_widget_timepicker/README.rst | 20 ++++----- web_widget_timepicker/__openerp__.py | 4 -- .../static/src/js/web_widget_timepicker.js | 42 ++++--------------- 3 files changed, 17 insertions(+), 49 deletions(-) diff --git a/web_widget_timepicker/README.rst b/web_widget_timepicker/README.rst index fa64c2df..b3b474c5 100644 --- a/web_widget_timepicker/README.rst +++ b/web_widget_timepicker/README.rst @@ -12,10 +12,9 @@ definition. It can be use as a replacement for the standard float_time widget. If you use the widget with a field record, the input field has the following default timepicker options: -* By default direct user input is disabled -* By default the possible selection is based on 15 minute interval -* By default 24 hour mode with H:i format -* Scroll selection defaults to current server time +* By default the possible selection is based on 15 minute interval (step: 15) +* By default 24 hour mode with H:i format (timeFormat: 'H:i') +* By default scroll selection starts at current time (scrollDefault: 'now') The widget uses the jquery.timepicker plugin by Jon Thornton @@ -24,22 +23,21 @@ Usage ===== This module defines a new widget type for form views input fileds. - Set the attribute ``widget=timepicker`` in a ``field`` tag in a form view. -You can pass all options through the "timepicker" field in the options:: +You can pass custom options through the "timepicker" field in the options attribute: ... - + ... -See the available options at https://github.com/jonthornton/jquery-timepicker#timepicker-plugin-for-jquery +See the available options at https://github.com/jonthornton/jquery-timepicker#timepicker-plugin-for-jquery. -ToDo -==== +Known issues / Roadmap +====================== -Sanity check on options available in field defintion as override options for timepicker widget. +* Absolutely no sanity check or validation on options. Credits diff --git a/web_widget_timepicker/__openerp__.py b/web_widget_timepicker/__openerp__.py index 251486cc..d2cdeb42 100644 --- a/web_widget_timepicker/__openerp__.py +++ b/web_widget_timepicker/__openerp__.py @@ -10,7 +10,6 @@ 'category': 'Web', 'website': 'https://github.com/OCA/Web', - # any module necessary for this one to work correctly 'depends': [ 'web' ], @@ -25,12 +24,9 @@ 'qweb': [ 'static/src/xml/web_widget_timepicker.xml' ], - - # always loaded 'data': [ 'views/web_widget_timepicker_assets.xml' ], - # Installation options "installable": True, } diff --git a/web_widget_timepicker/static/src/js/web_widget_timepicker.js b/web_widget_timepicker/static/src/js/web_widget_timepicker.js index 4db68f30..b56a8cf4 100644 --- a/web_widget_timepicker/static/src/js/web_widget_timepicker.js +++ b/web_widget_timepicker/static/src/js/web_widget_timepicker.js @@ -1,20 +1,11 @@ -odoo.define('web_widget_timepicker.form_widgets', function (require) { +odoo.define('web_widget_timepicker', function (require) { "use strict"; var core = require('web.core'); var formats = require('web.formats'); var common = require('web.form_common'); - // Snippet from http://stackoverflow.com/questions/9036429/convert-object-string-to-json - function cleanup_str2json(str) { - return (str.length > 0 && str !== undefined) ? str - // wrap keys without quote with valid double quote - .replace(/([\$\w]+)\s*:/g, function(_, $1){return '"'+$1+'":'}) - // replacing single quote wrapped ones to double quote - .replace(/'([^']+)'/g, function(_, $1){return '"'+$1+'"'}) : undefined; - }; - - var TimePicker = common.AbstractField.extend(common.ReinitializeFieldMixin, { + var TimePickerField = common.AbstractField.extend(common.ReinitializeFieldMixin, { is_field_number: true, template: "TimePickerField", internal_format: 'float_time', @@ -27,9 +18,7 @@ odoo.define('web_widget_timepicker.form_widgets', function (require) { this.internal_set_value(0); - this.options = _.defaults( {}, { - disableTextInput: true, - forceRoundTime: true, + this.options = _.defaults( this.options, { step: 15, selectOnBlur: true, timeFormat: 'H:i', @@ -38,24 +27,7 @@ odoo.define('web_widget_timepicker.form_widgets', function (require) { }, initialize_content: function() { if(!this.get("effective_readonly")) { - var custom_options; - - if( this.node.attrs['data-options'] !== undefined && this.node.attrs['data-options'].length > 0) { - // First try to use jquery data function to create object - custom_options = $(this.node).data('options'); - - if(typeof custom_options !== 'object') { - // No garantee that the input data-options string is valid JSON format so try to cleanup - custom_options = JSON.parse(cleanup_str2json(this.node.attrs['data-options'])); - } - } - - if(typeof custom_options === 'object') { - this.$el.find('input').timepicker($.extend({}, this.options, custom_options )); - } else { - this.$el.find('input').timepicker(this.options); - } - + this.$el.find('input').timepicker(this.options); this.setupFocus(this.$('input')); } }, @@ -109,7 +81,9 @@ odoo.define('web_widget_timepicker.form_widgets', function (require) { }, }); - core.form_widget_registry.add('timepicker', TimePicker); + core.form_widget_registry.add('timepicker', TimePickerField); - return TimePicker; + return { + TimePickerField: TimePickerField, + }; }); From 83117dc8ebdbbde722added58563897da851b7f4 Mon Sep 17 00:00:00 2001 From: docmfried Date: Fri, 20 May 2016 11:54:37 +0200 Subject: [PATCH 7/7] [FIX]: Change README.rst according to OCA template [IMP]: New module icon [ADD]: Screenshot / images to give an visual overview --- web_widget_timepicker/README.rst | 63 +++++++++++------- web_widget_timepicker/__openerp__.py | 45 ++++++------- web_widget_timepicker/images/form_view.png | Bin 0 -> 4281 bytes web_widget_timepicker/images/picker.png | Bin 0 -> 3868 bytes .../static/description/icon.png | Bin 9455 -> 13428 bytes 5 files changed, 61 insertions(+), 47 deletions(-) create mode 100644 web_widget_timepicker/images/form_view.png create mode 100644 web_widget_timepicker/images/picker.png diff --git a/web_widget_timepicker/README.rst b/web_widget_timepicker/README.rst index b3b474c5..fe0009b3 100644 --- a/web_widget_timepicker/README.rst +++ b/web_widget_timepicker/README.rst @@ -1,55 +1,72 @@ .. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :alt: License: AGPL-3 + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 -============================== -Timepicker widget in form view -============================== -This module defines a timepicker widget, to be used with float fields -or (function) fields. Use ``widget='timepicker'`` in your form view -definition. It can be use as a replacement for the standard float_time widget. +=============================== +Timepicker widget in form views +=============================== -If you use the widget with a field record, the input field has the following default -timepicker options: +This module provides a timepicker widget for float fields. +It can be used as a replacement for the standard float_time widget in form views. -* By default the possible selection is based on 15 minute interval (step: 15) -* By default 24 hour mode with H:i format (timeFormat: 'H:i') -* By default scroll selection starts at current time (scrollDefault: 'now') -The widget uses the jquery.timepicker plugin by Jon Thornton +|picker| + + +The widget has the following default timepicker options: + +* the possible selection is based on 15 minute interval (step: 15) +* 24 hour mode in H:i format (timeFormat: 'H:i') +* scroll selection starts at current time (scrollDefault: 'now') + + +|formview| Usage ===== -This module defines a new widget type for form views input fileds. -Set the attribute ``widget=timepicker`` in a ``field`` tag in a form view. +In the form view declaration, put widget='timepicker' attribute in the field tag:: -You can pass custom options through the "timepicker" field in the options attribute: + ... + +
+ ... + + + ... + + + ... + +Additional jquery-timepicker plugin options can be specified by an options attribute:: ... - + ... -See the available options at https://github.com/jonthornton/jquery-timepicker#timepicker-plugin-for-jquery. +See the available options at `jquery-timepicker `_. + +.. |picker| image:: ./images/picker.png +.. |formview| image:: ./images/form_view.png Known issues / Roadmap ====================== -* Absolutely no sanity check or validation on options. +* No validation on options. Credits ======= -Jon Thornton (https://cdnjs.com/libraries/jquery-timepicker) -jquery.timepicker plugin - This software is made available under the open source MIT License. © 2014 Jon Thornton and contributors +* The module uses the `jquery-timepicker `_ plugin by Jon Thornton. This software is made available under the open source MIT License. © 2014 Jon Thornton and contributors -Odoo Community Association (OCA) +* Odoo Community Association (OCA) Contributors ------------ -* Michael Fried +* Michael Fried diff --git a/web_widget_timepicker/__openerp__.py b/web_widget_timepicker/__openerp__.py index d2cdeb42..634dd98a 100644 --- a/web_widget_timepicker/__openerp__.py +++ b/web_widget_timepicker/__openerp__.py @@ -1,32 +1,29 @@ # -*- coding: utf-8 -*- -# © 2016 Michael Fried @ Vividlab () +# © 2016 Vividlab () # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - { - 'name': 'Web Timepicker Widget', - 'version': '9.0.1.0.0', - 'author': 'Michael Fried@Vividlab, Odoo Community Association (OCA)', - 'license': 'AGPL-3', - 'category': 'Web', - 'website': 'https://github.com/OCA/Web', - - 'depends': [ - 'web' - ], - 'css': [ - 'static/src/lib/jquery.timerpicker/jquery.timepicker.css', - 'static/src/css/web_widget_timepicker.css' + "name": "Web Timepicker Widget", + "version": "9.0.1.0.0", + "author": "VividLab, Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Web", + "website": "http://www.vividlab.de", + "installable": True, + "depends": [ + "web", ], - 'js': [ - 'static/src/lib/jquery.timerpicker/jquery.timepicker.js', - 'static/src/js/web_widget_timepicker.js', + "css": [ + "static/src/lib/jquery.timerpicker/jquery.timepicker.css", + "static/src/css/web_widget_timepicker.css", ], - 'qweb': [ - 'static/src/xml/web_widget_timepicker.xml' + "js": [ + "static/src/lib/jquery.timerpicker/jquery.timepicker.js", + "static/src/js/web_widget_timepicker.js", ], - 'data': [ - 'views/web_widget_timepicker_assets.xml' + "data": [ + "views/web_widget_timepicker_assets.xml", ], - - "installable": True, + "qweb": [ + "static/src/xml/web_widget_timepicker.xml", + ] } diff --git a/web_widget_timepicker/images/form_view.png b/web_widget_timepicker/images/form_view.png new file mode 100644 index 0000000000000000000000000000000000000000..1feae7a16f5819d2732226fb0a3d5e5d3e5f73f9 GIT binary patch literal 4281 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 literal 0 HcmV?d00001 diff --git a/web_widget_timepicker/static/description/icon.png b/web_widget_timepicker/static/description/icon.png index 3a0328b516c4980e8e44cdb63fd945757ddd132d..91216607781928984009d559a54563ea224c909c 100644 GIT binary patch literal 13428 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 E0Og;U=l}o! literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I