diff options
| author | Andrea Ulliana <aul@odoo.com> | 2018-11-27 09:19:45 +0000 |
|---|---|---|
| committer | qsm-odoo <qsm@odoo.com> | 2018-12-04 10:06:11 +0000 |
| commit | 1f847a1e2eed63086448ceb451bd1a2f87c8c641 (patch) | |
| tree | c8b321dd526200e4c520929b874bca5496f81b82 /addons/web_editor/static/src | |
| parent | 7d6a9924e5bb8ff14defeaa07002c08920222d7b (diff) | |
[IMP] web_editor, *: improve media dialog
* web_unsplash, website_sale
- Visual improvements and cleaning in each tab: video, documents,
pictogram, images.
- Only one search input for both the local library and unsplash.
- Replace the idea of multiple pages by adding a 'Load More' button.
task-1896705
Diffstat (limited to 'addons/web_editor/static/src')
| -rw-r--r-- | addons/web_editor/static/src/js/widgets/widgets.js | 300 | ||||
| -rw-r--r-- | addons/web_editor/static/src/scss/web_editor.ui.scss | 8 | ||||
| -rw-r--r-- | addons/web_editor/static/src/xml/editor.xml | 191 |
3 files changed, 280 insertions, 219 deletions
diff --git a/addons/web_editor/static/src/js/widgets/widgets.js b/addons/web_editor/static/src/js/widgets/widgets.js index 7c33ab53b97..daa5e868afd 100644 --- a/addons/web_editor/static/src/js/widgets/widgets.js +++ b/addons/web_editor/static/src/js/widgets/widgets.js @@ -4,7 +4,7 @@ odoo.define('web_editor.widget', function (require) { var ajax = require('web.ajax'); var base = require('web_editor.base'); var core = require('web.core'); -var Dialog = require('web.Dialog'); +var DialogBase = require('web.Dialog'); var Widget = require('web.Widget'); var weContext = require("web_editor.context"); @@ -26,7 +26,7 @@ function simulateMouseEvent(el, type) { /** * Extend Dialog class to handle save/cancel of edition components. */ -Dialog = Dialog.extend({ +var Dialog = DialogBase.extend({ /** * @constructor */ @@ -109,6 +109,9 @@ var AltDialog = Dialog.extend({ var MediaWidget = Widget.extend({ xmlDependencies: ['/web_editor/static/src/xml/editor.xml'], + events: { + 'input input#icon-search': '_onSearchInput', + }, /** * @constructor @@ -117,7 +120,7 @@ var MediaWidget = Widget.extend({ this._super.apply(this, arguments); this.media = media; this.$media = $(media); - this.page = 0; + this._onSearchInput = _.debounce(this._onSearchInput, 250); }, //-------------------------------------------------------------------------- @@ -145,12 +148,6 @@ var MediaWidget = Widget.extend({ }; }, /** - * @override - */ - goToPage: function (page) { - this.page = page; - }, - /** * @abstract * @param {string} needle * @returns {Deferred} @@ -178,6 +175,17 @@ var MediaWidget = Widget.extend({ this.$media = $media; this.media = $media[0]; }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + */ + _onSearchInput: function (ev) { + this.search($(ev.currentTarget).val() || ''); + }, }); /** @@ -185,19 +193,20 @@ var MediaWidget = Widget.extend({ */ var ImageWidget = MediaWidget.extend({ template: 'web_editor.dialog.image', - events: { - 'click .o_upload_image_button': '_onUploadButtonClick', - 'click .o_upload_image_button_no_optimization': '_onUploadButtonNoOptimizationClick', + events: _.extend({}, MediaWidget.prototype.events || {}, { + 'click .o_upload_media_button': '_onUploadButtonClick', + 'click .o_upload_media_button_no_optimization': '_onUploadButtonNoOptimizationClick', 'change input[type=file]': '_onImageSelection', - 'click .o_upload_image_url_button': '_onUploadURLButtonClick', - 'input input.url': "_onSearchInput", + 'click .o_upload_media_url_button': '_onUploadURLButtonClick', + 'input input.url': '_onChangeInputURL', 'click .existing-attachments [data-src]': '_onImageClick', 'dblclick .existing-attachments [data-src]': '_onImageDblClick', 'click .o_existing_attachment_remove': '_onRemoveClick', - }, + 'click .o_load_more': '_onLoadMore', + }), IMAGES_PER_ROW: 6, - IMAGES_ROWS: 2, + IMAGES_ROWS: 5, /** * @constructor @@ -205,7 +214,7 @@ var ImageWidget = MediaWidget.extend({ init: function (parent, media, options) { this._super.apply(this, arguments); - this.IMAGES_PER_PAGE = this.IMAGES_PER_ROW * this.IMAGES_ROWS; + this.IMAGES_DISPLAYED_TOTAL = this.IMAGES_PER_ROW * this.IMAGES_ROWS; this.options = options; this.accept = options.accept || (options.document ? '*/*' : 'image/*'); @@ -268,22 +277,6 @@ var ImageWidget = MediaWidget.extend({ /** * @override */ - getControlPanelConfig: function () { - return _.extend(this._super.apply(this, arguments), { - pagerLeftEnabled: this.page > 0, - pagerRightEnabled: (this.page + 1) * this.IMAGES_PER_PAGE < this.records.length, - }); - }, - /** - * @override - */ - goToPage: function (page) { - this._super.apply(this, arguments); - this._renderImages(); - }, - /** - * @override - */ save: function () { var self = this; if (this.multiImages) { @@ -444,16 +437,28 @@ var ImageWidget = MediaWidget.extend({ */ _renderImages: function () { var self = this; - var startIndex = this.page * this.IMAGES_PER_PAGE; var rows = _(this.records).chain() - .slice(startIndex, startIndex + this.IMAGES_PER_PAGE) + .slice(0, this.IMAGES_DISPLAYED_TOTAL) .groupBy(function (a, index) { return Math.floor(index / self.IMAGES_PER_ROW); }) .values() .value(); + var noMoreImgToLoad = this.IMAGES_DISPLAYED_TOTAL >= this.records.length; + this.$('.o_load_more').toggleClass('d-none', noMoreImgToLoad); + this.$('.o_load_done_msg').toggleClass('d-none', !noMoreImgToLoad); this.$('.form-text').empty(); - this.$('.existing-attachments').replaceWith(QWeb.render('web_editor.dialog.image.existing.content', {rows: rows})); + //Rendering menu & content for 'Image' or 'Document' tab + var templateMenu = 'web_editor.dialog.files.submenu'; + var templateContent = 'web_editor.dialog.files.existing.content'; + this.$('.o_submenu').replaceWith(QWeb.render(templateMenu, { + accept: this.accept, + isDocument: this.options.document, + })); + this.$('.existing-attachments').replaceWith(QWeb.render(templateContent, { + rows: rows, + isDocument: this.options.document, + })); var $divs = this.$('.o_image'); var imageDefs = _.map($divs, function (el) { @@ -479,12 +484,12 @@ var ImageWidget = MediaWidget.extend({ /** * @private */ - _toggleImage: function (attachment, clearSearch, force_select) { + _toggleImage: function (attachment, clearSearch, forceSelect) { if (this.multiImages) { var img = _.select(this.images, function (v) { return v.id === attachment.id; }); if (img.length) { - if (!force_select) { - this.images.splice(this.images.indexOf(img[0]),1); + if (!forceSelect) { + this.images.splice(this.images.indexOf(img[0]), 1); } } else { this.images.push(attachment); @@ -501,14 +506,14 @@ var ImageWidget = MediaWidget.extend({ /** * @private */ - _uploadImage: function () { + _uploadFile: function () { var self = this; /** * @todo file upload cannot be handled with _rpc smoothly. This uses the * form posting in iframe trick to handle the upload. */ - var $iframe = this.$('iframe'); + var $iframe = this.$('.o_file_upload_iframe'); $iframe.on('load', function () { var iWindow = $iframe[0].contentWindow; var attachments = iWindow.attachments; @@ -524,7 +529,7 @@ var ImageWidget = MediaWidget.extend({ _processFile(null, error || !attachments.length); } self.images = attachments; - for (var i = 0 ; i < attachments.length ; i++) { + for (var i = 0; i < attachments.length; i++) { _processFile(attachments[i], error); } @@ -533,22 +538,25 @@ var ImageWidget = MediaWidget.extend({ } function _processFile(attachment, error) { - var $button = self.$('.o_upload_image_button'); if (!error) { - $button.addClass('btn-success'); self._toggleImage(attachment, true); } else { - $button.addClass('btn-danger'); self.$el.addClass('o_has_error').find('.form-control, .custom-select').addClass('is-invalid'); self.$el.find('.form-text').text(error); } - - if (!self.multiImages) { - self.trigger_up('save_request'); - } } }); this.$el.submit(); + // Emptying file inputs + this.$('.o_file_input').val(''); + }, + /** + * @private + */ + _loadMoreImages: function () { + this.IMAGES_ROWS += 2; + this.IMAGES_DISPLAYED_TOTAL = this.IMAGES_ROWS * this.IMAGES_PER_ROW; + this._renderImages(); }, //-------------------------------------------------------------------------- @@ -558,12 +566,12 @@ var ImageWidget = MediaWidget.extend({ /** * @private */ - _onImageClick: function (ev, force_select) { + _onImageClick: function (ev, forceSelect) { var $img = $(ev.currentTarget); var attachment = _.find(this.records, function (record) { return record.id === $img.data('id'); }); - this._toggleImage(attachment, false, force_select); + this._toggleImage(attachment, false, forceSelect); }, /** * @private @@ -580,44 +588,70 @@ var ImageWidget = MediaWidget.extend({ this.$el.addClass('nosave'); $form.removeClass('o_has_error').find('.form-control, .custom-select').removeClass('is-invalid'); $form.find('.form-text').empty(); - this.$('.o_upload_image_button').removeClass('btn-danger btn-success'); - this._uploadImage(); + this.$('.o_upload_media_button').removeClass('btn-danger btn-success'); + this._uploadFile(); }, /** * @private */ _onRemoveClick: function (ev) { var self = this; - var $helpBlock = this.$('.form-text').empty(); - var $a = $(ev.currentTarget); - var id = parseInt($a.data('id'), 10); - var attachment = _.findWhere(this.records, {id: id}); - - return this._rpc({ - route: '/web_editor/attachment/remove', - params: { - ids: [id], - }, - }).then(function (prevented) { - if (_.isEmpty(prevented)) { - self.records = _.without(self.records, attachment); - self._renderImages(); - return; + var message = _t("Are you sure you want to delete this file ?"); + DialogBase.confirm(this, message, { + confirm_callback: function () { + var $helpBlock = this.$('.form-text').empty(); + var $a = $(ev.currentTarget); + var id = parseInt($a.data('id'), 10); + var attachment = _.findWhere(this.records, {id: id}); + + return self._rpc({ + route: '/web_editor/attachment/remove', + params: { + ids: [id], + }, + }).then(function (prevented) { + if (_.isEmpty(prevented)) { + self.records = _.without(self.records, attachment); + self.search(''); + return; + } + $helpBlock.replaceWith(QWeb.render('web_editor.dialog.image.existing.error', { + views: prevented[id] + })); + }); } - $helpBlock.replaceWith(QWeb.render('web_editor.dialog.image.existing.error', { - views: prevented[id] - })); }); }, /** * @private */ - _onSearchInput: function (ev) { + _onChangeInputURL: function (ev) { var $input = $(ev.currentTarget); var $button = $input.next('.input-group-append').children(); - var emptyValue = ($input.val() === ''); + var inputValue = $input.val(); + var emptyValue = (inputValue === ''); + var $warning = this.$('.warning-url'); + var $success = this.$('.success-url'); + var $error = this.$('.error-url'); + var $btn = this.$('.o_upload_media_url_button'); + var regex = /^(?:https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/; + var isURL = regex.test(inputValue); + var showWarningOrError = (inputValue.length !== 0 ? isURL : !isURL); + $button.toggleClass('btn-secondary', emptyValue).toggleClass('btn-primary', !emptyValue) .prop('disabled', emptyValue); + + if (!this.options.document) { + _.each(['.gif', '.jpe', '.jpg', '.png'], function (format) { + var endsWith = inputValue.endsWith(format); + $button.text(endsWith ? _t("Add image") : _t("Add as document")); + }); + } else { + $btn.prop('disabled', !isURL); + } + $success.toggleClass('d-none', !isURL); + $warning.toggleClass('d-none', showWarningOrError); + $error.toggleClass('d-none', showWarningOrError); }, /** * @private @@ -630,13 +664,19 @@ var ImageWidget = MediaWidget.extend({ */ _onUploadButtonNoOptimizationClick: function () { this.$('input[name="disable_optimization"]').val('1'); - this.$('.o_upload_image_button').click(); + this.$('.o_upload_media_button').click(); }, /** * @private */ _onUploadURLButtonClick: function () { - this._uploadImage(); + this._uploadFile(); + }, + /** + * @private + */ + _onLoadMore: function () { + this._loadMoreImages(); }, }); @@ -646,10 +686,10 @@ var ImageWidget = MediaWidget.extend({ */ var IconWidget = MediaWidget.extend({ template: 'web_editor.dialog.font-icons', - events : { + events: _.extend({}, MediaWidget.prototype.events || {}, { 'click .font-icons-icon': '_onIconClick', 'dblclick .font-icons-icon': '_onIconDblClick', - }, + }), /** * @constructor @@ -669,7 +709,7 @@ var IconWidget = MediaWidget.extend({ start: function () { this.$icons = this.$('.font-icons-icon'); var classes = (this.media && this.media.className || '').split(/\s+/); - for (var i = 0 ; i < classes.length ; i++) { + for (var i = 0; i < classes.length; i++) { var cls = classes[i]; if (_.contains(this.alias, cls)) { this.selectedIcon = cls; @@ -753,12 +793,12 @@ var IconWidget = MediaWidget.extend({ */ _getFont: function (classNames) { if (!(classNames instanceof Array)) { - classNames = (classNames||"").split(/\s+/); + classNames = (classNames || "").split(/\s+/); } var fontIcon, cssData; - for (var k=0; k<this.iconsParser.length; k++) { + for (var k = 0; k < this.iconsParser.length; k++) { fontIcon = this.iconsParser[k]; - for (var s=0; s<fontIcon.cssData.length; s++) { + for (var s = 0; s < fontIcon.cssData.length; s++) { cssData = fontIcon.cssData[s]; if (_.intersection(classNames, cssData.names).length) { return { @@ -809,7 +849,7 @@ var IconWidget = MediaWidget.extend({ */ var VideoWidget = MediaWidget.extend({ template: 'web_editor.dialog.video', - events : { + events: { 'change .o_video_dialog_options input': '_onUpdateVideoOption', 'input textarea#o_video_text': '_onVideoCodeInput', 'change textarea#o_video_text': '_onVideoCodeChange', @@ -867,10 +907,10 @@ var VideoWidget = MediaWidget.extend({ this._updateVideo(); if (this.$('.o_video_dialog_iframe').is('iframe')) { this._replaceMedia($( - '<div class="media_iframe_video" data-oe-expression="' + this.$content.attr('src') + '">'+ - '<div class="css_editable_mode_display"> </div>'+ - '<div class="media_iframe_video_size" contenteditable="false"> </div>'+ - '<iframe src="' + this.$content.attr('src') + '" frameborder="0" contenteditable="false"></iframe>'+ + '<div class="media_iframe_video" data-oe-expression="' + this.$content.attr('src') + '">' + + '<div class="css_editable_mode_display"> </div>' + + '<div class="media_iframe_video_size" contenteditable="false"> </div>' + + '<iframe src="' + this.$content.attr('src') + '" frameborder="0" contenteditable="false"></iframe>' + '</div>' )); } @@ -950,12 +990,12 @@ var VideoWidget = MediaWidget.extend({ $video.attr('src', '//player.vimeo.com/video/' + vimMatch[3] + autoplay); videoType = 'vim'; } else if (dmMatch && dmMatch[2].length) { - var just_id = dmMatch[2].replace('video/',''); - $video.attr('src', '//www.dailymotion.com/embed/video/' + just_id + autoplay); + var justId = dmMatch[2].replace('video/', ''); + $video.attr('src', '//www.dailymotion.com/embed/video/' + justId + autoplay); videoType = 'dm'; } else if (ykuMatch && ykuMatch[3].length) { - var yku_id = ykuMatch[3].indexOf('.html?') >= 0 ? ykuMatch[3].substring(0, ykuMatch[3].indexOf('.html?')) : ykuMatch[3]; - $video.attr('src', '//player.youku.com/embed/' + yku_id); + var ykuId = ykuMatch[3].indexOf('.html?') >= 0 ? ykuMatch[3].substring(0, ykuMatch[3].indexOf('.html?')) : ykuMatch[3]; + $video.attr('src', '//player.youku.com/embed/' + ykuId); videoType = 'yku'; } else { return {errorCode: 1}; @@ -994,7 +1034,7 @@ var VideoWidget = MediaWidget.extend({ // Reset the feedback this.$content.empty(); this.$('#o_video_form_group').removeClass('o_has_error o_has_success').find('.form-control, .custom-select').removeClass('is-invalid is-valid'); - this.$('.o_video_dialog_options li').addClass('d-none'); + this.$('.o_video_dialog_options div').addClass('d-none'); // Check video code var $textarea = this.$('textarea#o_video_text'); @@ -1005,7 +1045,7 @@ var VideoWidget = MediaWidget.extend({ // Detect if we have an embed code rather than an URL var embedMatch = code.match(/(src|href)=["']?([^"']+)?/); - if (embedMatch && embedMatch[2].length > 0 && embedMatch[2].indexOf('instagram')){ + if (embedMatch && embedMatch[2].length > 0 && embedMatch[2].indexOf('instagram')) { embedMatch[1] = embedMatch[2]; // Instagram embed code is different } var url = embedMatch ? embedMatch[1] : code; @@ -1020,10 +1060,10 @@ var VideoWidget = MediaWidget.extend({ hide_dm_share: this.$('input#o_video_hide_dm_share').is(':checked'), }); - var $opt_box = this.$('.o_video_dialog_options'); + var $optBox = this.$('.o_video_dialog_options'); // Show / Hide preview elements - this.$el.find('.o_video_dialog_preview_text, .media_iframe_video_size').add($opt_box).toggleClass('d-none', !query.$video); + this.$el.find('.o_video_dialog_preview_text, .media_iframe_video_size').add($optBox).toggleClass('d-none', !query.$video); // Toggle validation classes this.$el.find('#o_video_form_group') .toggleClass('o_has_error', !query.$video).find('.form-control, .custom-select').toggleClass('is-invalid', !query.$video) @@ -1031,15 +1071,16 @@ var VideoWidget = MediaWidget.extend({ .toggleClass('o_has_success', !!query.$video).find('.form-control, .custom-select').toggleClass('is-valid', !!query.$video); // Individually show / hide options base on the video provider - $opt_box.find('li.o_' + query.type + '_option').removeClass('d-none'); + $optBox.find('div.o_' + query.type + '_option').removeClass('d-none'); // Hide the entire options box if no options are available - $opt_box.toggleClass('d-none', $opt_box.find('li:not(.d-none)').length === 0); + $optBox.toggleClass('d-none', $optBox.find('div:not(.d-none)').length === 0); + $('.option_title').toggleClass('d-none'); if (query.type === 'yt') { // Youtube only: If 'hide controls' is checked, hide 'fullscreen' // and 'youtube logo' options too - this.$('input#o_video_hide_fullscreen, input#o_video_hide_yt_logo').closest('li').toggleClass('d-none', this.$('input#o_video_hide_controls').is(':checked')); + this.$('input#o_video_hide_fullscreen, input#o_video_hide_yt_logo').closest('div').toggleClass('d-none', this.$('input#o_video_hide_controls').is(':checked')); } var $content = query.$video; @@ -1047,13 +1088,13 @@ var VideoWidget = MediaWidget.extend({ switch (query.errorCode) { case 0: $content = $('<div/>', { - class: 'alert alert-danger o_video_dialog_iframe', + class: 'alert alert-danger o_video_dialog_iframe mb-2 mt-2', text: _t("The provided url is not valid"), }); break; case 1: $content = $('<div/>', { - class: 'alert alert-warning o_video_dialog_iframe', + class: 'alert alert-warning o_video_dialog_iframe mb-2 mt-2', text: _t("The provided url does not reference any supported video"), }); break; @@ -1105,14 +1146,11 @@ var MediaDialog = Dialog.extend({ xmlDependencies: Dialog.prototype.xmlDependencies.concat( ['/web_editor/static/src/xml/editor.xml'] ), - events : _.extend({}, Dialog.prototype.events, { - 'input input#icon-search': '_onSearchInput', + events: _.extend({}, Dialog.prototype.events, { 'shown.bs.tab a[data-toggle="tab"]': '_onTabChange', - 'click .previous:not(.disabled), .next:not(.disabled)': '_onPagerClick', }), custom_events: _.extend({}, Dialog.prototype.custom_events || {}, { save_request: '_onSaveRequest', - update_control_panel: '_updateControlPanel', }), /** @@ -1122,6 +1160,7 @@ var MediaDialog = Dialog.extend({ var self = this; this._super(parent, _.extend({}, { title: _t("Select a Media"), + save_text: _t("Add"), }, options)); if ($editable) { @@ -1168,8 +1207,6 @@ var MediaDialog = Dialog.extend({ } self.$('[href="#editor-media-' + tabToShow + '"]').tab('show'); }); - - this._onSearchInput = _.debounce(this._onSearchInput, 250); }, /** * @override @@ -1266,18 +1303,6 @@ var MediaDialog = Dialog.extend({ */ _setActive: function (widget) { this.active = widget; - this.active.goToPage(0); - this._updateControlPanel(); - }, - /** - * @private - */ - _updateControlPanel: function () { - var cpConfig = this.active.getControlPanelConfig(); - this.$('li.search').toggleClass('d-none', !cpConfig.searchEnabled); - this.$('.previous, .next').toggleClass('d-none', !cpConfig.pagerEnabled); - this.$('.previous').toggleClass("disabled", !cpConfig.pagerLeftEnabled); - this.$('.next').toggleClass("disabled", !cpConfig.pagerRightEnabled); }, //-------------------------------------------------------------------------- @@ -1287,13 +1312,6 @@ var MediaDialog = Dialog.extend({ /** * @private */ - _onPagerClick: function (ev) { - this.active.goToPage(this.active.page + ($(ev.currentTarget).hasClass('previous') ? -1 : 1)); - this._updateControlPanel(); - }, - /** - * @private - */ _onSaveRequest: function (ev) { ev.stopPropagation(); this.save(); @@ -1301,16 +1319,6 @@ var MediaDialog = Dialog.extend({ /** * @private */ - _onSearchInput: function (ev) { - var self = this; - this.active.goToPage(0); - this.active.search($(ev.currentTarget).val() || '').then(function () { - self._updateControlPanel(); - }); - }, - /** - * @private - */ _onTabChange: function (ev) { var $target = $(ev.target); if ($target.is('[href="#editor-media-image"]')) { @@ -1359,7 +1367,7 @@ var LinkDialog = Dialog.extend({ this.data.iniClassName = $(this.data.range.sc).filter("a").attr("class") || ""; this.data.className = this.data.iniClassName.replace(/(^|\s+)btn(-[a-z0-9_-]*)?/gi, ' '); - var is_link = this.data.range.isOnAnchor(); + var isLink = this.data.range.isOnAnchor(); var sc = r.sc; var so = r.so; @@ -1367,7 +1375,7 @@ var LinkDialog = Dialog.extend({ var eo = r.eo; var nodes; - if (!is_link) { + if (!isLink) { if (sc.tagName) { sc = dom.firstChild(so ? sc.childNodes[so] : sc); so = 0; @@ -1381,7 +1389,7 @@ var LinkDialog = Dialog.extend({ so = 0; } if (ec.tagName) { - ec = dom.lastChild(eo ? ec.childNodes[eo-1] : ec); + ec = dom.lastChild(eo ? ec.childNodes[eo - 1] : ec); eo = ec.textContent.length; } else if (eo !== ec.textContent.length) { ec.splitText(eo); @@ -1391,11 +1399,11 @@ var LinkDialog = Dialog.extend({ // browsers can't target a picture or void node if (dom.isVoid(sc) || dom.isImg(sc)) { - so = dom.listPrev(sc).length-1; + so = dom.listPrev(sc).length - 1; sc = sc.parentNode; } if (dom.isBR(ec)) { - eo = dom.listPrev(ec).length-1; + eo = dom.listPrev(ec).length - 1; ec = ec.parentNode; } else if (dom.isVoid(ec) || dom.isImg(sc)) { eo = dom.listPrev(ec).length; @@ -1414,15 +1422,15 @@ var LinkDialog = Dialog.extend({ if (nodes.length > 1 || dom.ancestor(nodes[0], dom.isImg)) { var text = ""; this.data.images = []; - for (var i=0; i<nodes.length; i++) { + for (var i = 0; i < nodes.length; i++) { if (dom.ancestor(nodes[i], dom.isImg)) { this.data.images.push(dom.ancestor(nodes[i], dom.isImg)); text += '[IMG]'; - } else if (!is_link && nodes[i].nodeType === 1) { + } else if (!isLink && nodes[i].nodeType === 1) { // just use text nodes from listBetween - } else if (!is_link && i===0) { + } else if (!isLink && i === 0) { text += nodes[i].textContent.slice(so, Infinity); - } else if (!is_link && i===nodes.length-1) { + } else if (!isLink && i === nodes.length - 1) { text += nodes[i].textContent.slice(0, eo); } else { text += nodes[i].textContent; @@ -1534,7 +1542,7 @@ var LinkDialog = Dialog.extend({ var label = this.$('input[name="label"]').val() || url; if (label && this.data.images) { - for (var i = 0 ; i < this.data.images.length ; i++) { + for (var i = 0; i < this.data.images.length; i++) { label = label.replace(/</, "<").replace(/>/, ">").replace(/\[IMG\]/, this.data.images[i].outerHTML); } } @@ -1600,7 +1608,7 @@ var CropImageDialog = Dialog.extend({ cssLibs: [ '/web_editor/static/lib/cropper/css/cropper.css', ], - events : _.extend({}, Dialog.prototype.events, { + events: _.extend({}, Dialog.prototype.events, { 'click .o_crop_options [data-event]': '_onCropOptionClick', }), @@ -1683,7 +1691,7 @@ var CropImageDialog = Dialog.extend({ if (this.$cropperImage.length) { var data = this.$media.data(); var ratio = 0; - for (var i = 0 ; i < this.aspectRatioList.length ; i++) { + for (var i = 0; i < this.aspectRatioList.length; i++) { if (this.aspectRatioList[i][1] === data.aspectRatio) { ratio = this.aspectRatioList[i][2]; break; diff --git a/addons/web_editor/static/src/scss/web_editor.ui.scss b/addons/web_editor/static/src/scss/web_editor.ui.scss index 3581df3bf8b..805ac4a35b6 100644 --- a/addons/web_editor/static/src/scss/web_editor.ui.scss +++ b/addons/web_editor/static/src/scss/web_editor.ui.scss @@ -1386,10 +1386,16 @@ body .modal { margin-right: 10px; } } + .o_size_fa_icon { + font-size: 1.4rem; + } + .o_size_dropdown_item { + font-size: 0.9rem; + } } // VIDEO TAB - #editor-media-video .card { + #editor-media-video .o_video_dialog_container { padding: 0; display: flex; flex-flow: row wrap; diff --git a/addons/web_editor/static/src/xml/editor.xml b/addons/web_editor/static/src/xml/editor.xml index 631bd069e8b..0fc6434f0aa 100644 --- a/addons/web_editor/static/src/xml/editor.xml +++ b/addons/web_editor/static/src/xml/editor.xml @@ -57,18 +57,6 @@ <li class="nav-item"><a role="tab" href="#editor-media-document" aria-controls="editor-media-document" data-toggle="tab" t-attf-class="nav-link#{widget.noDocuments ? ' d-none' : ''}">Document</a></li> <li class="nav-item"><a role="tab" href="#editor-media-icon" aria-controls="editor-media-icon" data-toggle="tab" t-attf-class="nav-link#{widget.noIcons ? ' d-none' : ''}">Pictogram</a></li> <li class="nav-item"><a role="tab" href="#editor-media-video" aria-controls="editor-media-video" data-toggle="tab" t-attf-class="nav-link#{widget.noVideos ? ' d-none' : ''}">Video</a></li> - <li class="nav-item ml-auto search"> - <div class="form-group"> - <input class="form-control" id="icon-search" type="search"/> - <span class="fa fa-search" title="Search" role="img" aria-label="Search"/> - </div> - </li> - <li class="nav-item search"> - <div class="btn-group"> - <a role="button" class="btn btn-secondary disabled previous" href="#"><i class="fa fa-angle-left"/> Previous</a> - <a role="button" class="btn btn-secondary disabled next" href="#">Next <i class="fa fa-angle-right"/></a> - </div> - </li> </ul> <!-- Tab panes --> <div class="tab-content"> @@ -91,37 +79,81 @@ <input type="hidden" name="filters" t-att-value="widget.firstFilters.join('_')"/> <input t-if="widget.options.res_id" type="hidden" name="res_id" t-att-value="widget.options.res_id"/> <input t-if="widget.options.res_model" type="hidden" name="res_model" t-att-value="widget.options.res_model"/> - <div class="o_media_image_card card w-100"> - <div class="card-body p-0"> - <div class="form-inline"> - <input type="file" class="d-none" name="upload" t-att-accept="widget.accept" multiple="multiple"/> + <div class="mt-4 w-100"> + <div class="mb-4"> + <div class="o_submenu"></div> + </div> + <div class="form-text"/> + <div class="existing-attachments"> + <t t-call="web_editor.dialog.image.existing"/> + </div> + <div class="mt-4 text-center o_load_elements mx-auto"> + <button class="btn btn-primary o_load_more" type="button">Load more...</button> + <div class="mt-4 o_load_done_msg d-none"> + <span><i>All attachments have been loaded</i></span> + </div> + </div> + </div> + <iframe class="o_file_upload_iframe d-none" t-att-name="iframeName"/> + </form> + </t> + <t t-name="web_editor.dialog.files.submenu"> + <div class="o_submenu"> + <div class="row"> + <div class="col-sm"> + <div class="form-inline row no-gutters"> + <input type="file" class="d-none o_file_input" name="upload" t-att-accept="accept" multiple="multiple"/> <input type="hidden" name="disable_optimization" value=""/> - <div class="btn-group"> - <button class="btn btn-primary o_upload_image_button" type="button">Upload an image</button> - <button class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" type="button"> - <span class="sr-only">Alternate Upload</span> + <div class="btn-group o_btn_group_image"> + <button class="btn btn-primary o_upload_media_button" type="button"> + <t t-if="isDocument">Upload a document</t> + <t t-else="">Upload an image</t> </button> - <div class="dropdown-menu" role="menu"> - <a role="menuitem" class="dropdown-item o_upload_image_button_no_optimization">Upload image without optimization</a> - </div> + <t t-if="!isDocument"> + <button class="btn btn-primary dropdown-toggle dropdown-toggle-split o_media_optimization" data-toggle="dropdown" type="button"> + <span class="sr-only">Alternate Upload</span> + </button> + <div class="dropdown-menu" role="menu"> + <a role="menuitem" class="dropdown-item o_upload_media_button_no_optimization">Upload image without optimization</a> + </div> + </t> </div> - <span class="text-muted">— or —</span> - <label for="imageurl">Add an image URL</label> - <div class="input-group"> - <input type="text" class="form-control url" id="imageurl" name="url" placeholder="https://www.odoo.com/logo.png" style="width: 250px;"/> - <div class="input-group-append"> - <button type="button" class="btn btn-secondary o_upload_image_url_button" disabled="disabled">Add</button> + <div class="input-group col-8 ml-2"> + <input type="text" class="form-control url col-7" name="url" t-attf-placeholder="https://www.odoo.com/#{isDocument ? 'mydocument' : 'logo.png'}"/> + <div class="input-group-append ml-2"> + <button type="button" class="btn btn-sm btn-secondary o_upload_media_url_button" disabled="disabled"> + <t t-if="isDocument">Add document</t> + <t t-else="">Add image</t> + </button> + </div> + <div class="ml-2 o_size_fa_icon"> + <span class="success-url text-success d-none fa fa-check" title="The URL seems valid"/> + <t t-if="isDocument"> + <span class="error-url text-danger d-none fa fa-times" title="This URL does not seem to work"/> + </t> + <t t-else=""> + <span class="warning-url text-warning d-none fa fa-warning" title="The URL provided does not contain any image. It will be added in the document section."/> + </t> </div> </div> </div> </div> + <div class="col-sm text-right"> + <ul class="list-unstyled row"> + <li class="nav-item ml-auto col-6 search"> + <div class="form-group o_search_div"> + <t t-set="searchPlaceholder"> + <t t-if="isDocument">Search a document</t> + <t t-else="">Search an image</t> + </t> + <input class="form-control w-100" id="icon-search" type="search" t-att-placeholder="searchPlaceholder.trim()"/> + <span class="fa fa-search" title="Search" role="img" aria-label="Search"/> + </div> + </li> + </ul> + </div> </div> - <div class="form-text"/> - <div class="existing-attachments"> - <t t-call="web_editor.dialog.image.existing"/> - </div> - <iframe class="d-none" t-att-name="iframeName"/> - </form> + </div> </t> <t t-name="web_editor.dialog.image.existing"> <div role="dialog" class="modal" tabindex="-1"> @@ -136,19 +168,22 @@ <div class="form-text"/> </main> <footer class="modal-footer"> - <a data-dismiss="modal" href="#" aria-label="Discard">Discard</a> + <button data-dismiss="modal" type="button" aria-label="Discard">Discard</button> </footer> </div> </div> </div> </t> - <t t-name="web_editor.dialog.image.existing.content"> + <t t-name="web_editor.dialog.files.existing.content"> <div class="existing-attachments"> - <div class="row mt16" t-as="row" t-foreach="rows"> + <div class="row mt16 ml-auto" t-as="row" t-foreach="rows"> <div class="col-2 o_existing_attachment_cell" t-as="attachment" t-foreach="row"> - <i t-if="attachment.res_model === 'ir.ui.view'" class="fa fa-times o_existing_attachment_remove" title="This file is a public view attachment" role="img" aria-label="This file is a public view attachment" t-att-data-id="attachment.id"/> - <i t-else="" class="fa fa-times o_existing_attachment_remove" title="This file is attached to the current record" role="img" aria-label="This file is attached to the current record" t-att-data-id="attachment.id"/> + <i t-if="attachment.res_model === 'ir.ui.view'" class="fa fa-trash o_existing_attachment_remove" title="This file is a public view attachment" role="img" aria-label="This file is a public view attachment" t-att-data-id="attachment.id"/> + <i t-else="" class="fa fa-trash o_existing_attachment_remove" title="This file is attached to the current record" role="img" aria-label="This file is attached to the current record" t-att-data-id="attachment.id"/> <div class="o_attachment_border" t-att-style="attachment.res_model === 'ir.ui.view' ? null : 'border: 1px solid #5cb85c;'"><div t-att-data-src="attachment.src" t-att-data-url="attachment.url" role="img" t-att-aria-label="attachment.name" t-att-title="attachment.name" t-att-data-id="attachment.id" t-att-data-mimetype="attachment.mimetype" class="o_image o_image_loading"/></div> + <t t-if="isDocument"> + <small class="o_file_name"><t t-esc="attachment.name"/></small> + </t> </div> </div> </div> @@ -169,7 +204,15 @@ <!-- Icon choosing part of the Media Dialog --> <t t-name="web_editor.dialog.font-icons"> - <form action="#"> + <form action="#" class="mt-2"> + <div class="d-flex flex-row-reverse"> + <li class="nav-item search list-unstyled col-3 p-2"> + <div class="form-group"> + <input class="form-control" id="icon-search" type="search" placeholder="Search a pictogram"/> + <span class="fa fa-search" title="Search" role="img" aria-label="Search"/> + </div> + </li> + </div> <div class="font-icons-icons"> <t t-call="web_editor.dialog.font-icons.icons"> <t t-set="iconsParser" t-value="widget.iconsParser"/> @@ -189,42 +232,46 @@ </t> <!-- Video choosing part of the Media Dialog --> - <div t-name="web_editor.dialog.video" class="card"> - <form action="#" class="form-inline card-body"> + <div t-name="web_editor.dialog.video" class="o_video_dialog_container mt-4"> + <form action="#" class="form-inline w-100"> <div class="o_video_dialog_form"> - <div class="form-group" id="o_video_form_group"> - <label class="mt8" for="o_video_text">Video code <small class="text-muted">(URL or Embed)</small></label> + <div class="form-group mb-2" id="o_video_form_group"> + <label>Video code </label> + <small class="ml-1 form-text text-muted"> (URL or Embed)</small> + <label class="ml-2 col-form-label o_validate_feedback position-static" for="o_video_text"> + <i class="fa fa-check text-success" role="img" aria-label="Checked" title="Checked"/> + <i class="fa fa-exclamation-triangle text-danger" role="img" aria-label="Attention" title="Attention"/> + </label> <textarea class="form-control url" id="o_video_text" placeholder="Copy-paste your URL or embed code here"/> - <label class="col-form-label o_validate_feedback" for="o_video_text"><i class="fa fa-check" role="img" aria-label="Checked" title="Checked"/><i class="fa fa-exclamation-triangle" role="img" aria-label="Attention" title="Attention"/></label> </div> <div class="text-right"> <small class="text-muted">Accepts <b><i>Youtube</i></b>, <b><i>Instagram</i></b>, <b><i>Vine.co</i></b>, <b><i>Vimeo</i></b>, <b><i>Dailymotion</i></b> and <b><i>Youku</i></b> videos</small> </div> - <div class="o_video_dialog_options d-none mt48"> - <small class="text-muted">Options</small> - <ul class="list-group"> - <li class="list-group-item o_yt_option o_vim_option o_dm_option"> - <label class="o_switch mb0"><input id="o_video_autoplay" type="checkbox"/><span/>Autoplay</label> - </li> - <li class="list-group-item o_yt_option o_vim_option"> - <label class="o_switch mb0"><input id="o_video_loop" type="checkbox"/><span/>Loop</label> - </li> - <li class="list-group-item o_yt_option o_dm_option"> - <label class="o_switch mb0"><input id="o_video_hide_controls" type="checkbox"/><span/>Hide player controls</label> - </li> - <li class="list-group-item o_yt_option"> - <label class="o_switch mb0"><input id="o_video_hide_fullscreen" type="checkbox"/><span/>Hide fullscreen button</label> - </li> - <li class="list-group-item o_yt_option"> - <label class="o_switch mb0"><input id="o_video_hide_yt_logo" type="checkbox"/><span/>Hide Youtube logo</label> - </li> - <li class="list-group-item o_dm_option"> - <label class="o_switch mb0"><input id="o_video_hide_dm_logo" type="checkbox"/><span/>Hide Dailymotion logo</label> - </li> - <li class="list-group-item o_dm_option"> - <label class="o_switch mb0"><input id="o_video_hide_dm_share" type="checkbox"/><span/>Hide sharing button</label> - </li> - </ul> + <div class="o_video_dialog_options d-none mt-4"> + <div class="option_title mb-2 d-flex"> + <label>Options</label> + </div> + <div class="o_yt_option o_vim_option o_dm_option d-flex"> + <label class="o_switch mb0"><input id="o_video_autoplay" type="checkbox"/><span/>Autoplay</label> + </div> + <div class="o_yt_option o_vim_option d-flex"> + <label class="o_switch mb0"><input id="o_video_loop" type="checkbox"/><span/>Loop</label> + </div> + <div class="o_yt_option o_dm_option d-flex"> + <label class="o_switch mb0"><input id="o_video_hide_controls" type="checkbox"/><span/>Hide player controls</label> + </div> + <div class="o_yt_option d-flex"> + <label class="o_switch mb0"><input id="o_video_hide_fullscreen" type="checkbox"/><span/>Hide fullscreen button</label> + </div> + <div class="o_yt_option d-flex"> + <label class="o_switch mb0"><input id="o_video_hide_yt_logo" type="checkbox"/><span/>Hide Youtube logo</label> + </div> + <div class="o_dm_option d-flex"> + <label class="o_switch mb0"><input id="o_video_hide_dm_logo" type="checkbox"/><span/>Hide Dailymotion logo</label> + </div> + <div class="o_dm_option d-flex"> + <label class="o_switch mb0"><input id="o_video_hide_dm_share" type="checkbox"/><span/>Hide sharing button</label> + </div> </div> </div> <div id="video-preview"> @@ -307,7 +354,7 @@ <div class="form-group text-center"> <label>Preview</label> <div style="overflow: auto; max-width: 100%; max-height: 200px;"> - <a href="#" id="link-preview" aria-label="Preview" title="Preview"/> + <a href="#" type="button" id="link-preview" aria-label="Preview" title="Preview"/> </div> </div> </div> |
