diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js
index 3b3b6f8fd86..417d0daabb2 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -453,7 +453,7 @@ var SnippetEditor = Widget.extend({
this,
val.base_target ? this.$target.find(val.base_target).eq(0) : this.$target,
this.$el,
- val.data,
+ _.extend({snippetName: this._getName()}, val.data),
this.options
);
var key = optionName || _.uniqueId('option');
@@ -813,7 +813,7 @@ var SnippetsMenu = Widget.extend({
if (!$target.closest('body > *').length) {
return;
}
- if ($target.closest('#web_editor-top-edit, #oe_snippets, #oe_manipulators, .o_technical_modal, .oe_drop_zone').length) {
+ if ($target.closest('#web_editor-top-edit, #oe_snippets, #oe_manipulators, .o_technical_modal, .oe_drop_zone, .o_notification_manager').length) {
return;
}
this._activateSnippet($target);
diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js
index c3ea729d28d..cb94093c731 100644
--- a/addons/website/static/src/js/editor/snippets.options.js
+++ b/addons/website/static/src/js/editor/snippets.options.js
@@ -1547,13 +1547,31 @@ options.registry.topMenuColor = options.registry.colorpicker.extend({
/**
* Handles the edition of snippet's anchor name.
*/
-options.registry.anchorName = options.Class.extend({
+options.registry.anchor = options.Class.extend({
xmlDependencies: ['/website/static/src/xml/website.editor.xml'],
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
+ /**
+ * @override
+ */
+ start: function () {
+ // Generate anchor and copy it to clipboard on click, show the tooltip on success
+ this.$button = this.$el.find('we-button');
+ const clipboard = new ClipboardJS(this.$button[0], {text: () => this._getAnchorLink()});
+ clipboard.on('success', () => {
+ const anchor = decodeURIComponent(this._getAnchorLink());
+ this.displayNotification({
+ title: _t("Copied !"),
+ message: _.str.sprintf(_t("The anchor has been copied to your clipboard.
Link: %s"), anchor),
+ buttons: [{text: _t("edit"), click: () => this.openAnchorDialog()}],
+ });
+ });
+
+ return this._super.apply(this, arguments);
+ },
/**
* @override
*/
@@ -1568,20 +1586,19 @@ options.registry.anchorName = options.Class.extend({
},
//--------------------------------------------------------------------------
- // Options
+ // Private
//--------------------------------------------------------------------------
-
/**
* @see this.selectClass for parameters
*/
openAnchorDialog: function (previewMode, value, $opt) {
var self = this;
var buttons = [{
- text: _t("Save"),
+ text: _t("Save & copy"),
classes: 'btn-primary',
click: function () {
var $input = this.$('.o_input_anchor_name');
- var anchorName = $input.val().trim().replace(/\s/g, '_');
+ var anchorName = self._text2Anchor($input.val());
if (self.$target[0].id === anchorName) {
// If the chosen anchor name is already the one used by the
// element, close the dialog and do nothing else
@@ -1589,15 +1606,13 @@ options.registry.anchorName = options.Class.extend({
return;
}
- var isValid = /^[\w-]+$/.test(anchorName);
- var alreadyExists = isValid && $('#' + anchorName).length > 0;
- var anchorOK = isValid && !alreadyExists;
- this.$('.o_anchor_not_valid').toggleClass('d-none', isValid);
+ const alreadyExists = !!document.getElementById(anchorName);
this.$('.o_anchor_already_exists').toggleClass('d-none', !alreadyExists);
- $input.toggleClass('is-invalid', !anchorOK);
- if (anchorOK) {
+ $input.toggleClass('is-invalid', alreadyExists);
+ if (!alreadyExists) {
self._setAnchorName(anchorName);
this.close();
+ self.$button[0].click();
}
},
}, {
@@ -1618,16 +1633,11 @@ options.registry.anchorName = options.Class.extend({
new Dialog(this, {
title: _t("Link Anchor"),
$content: $(qweb.render('website.dialog.anchorName', {
- currentAnchor: this.$target.attr('id'),
+ currentAnchor: decodeURIComponent(this.$target.attr('id')),
})),
buttons: buttons,
}).open();
},
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
/**
* @private
* @param {String} value
@@ -1643,6 +1653,35 @@ options.registry.anchorName = options.Class.extend({
}
this.$target.trigger('content_changed');
},
+ /**
+ * Returns anchor text.
+ *
+ * @private
+ * @returns {string}
+ */
+ _getAnchorLink: function () {
+ if (!this.$target[0].id) {
+ const $titles = this.$target.find('h1, h2, h3, h4, h5, h6');
+ const title = $titles.length > 0 ? $titles[0].innerText : this.data.snippetName;
+ const anchorName = this._text2Anchor(title);
+ let n = '';
+ while (document.getElementById(anchorName + n)) {
+ n = (n || 1) + 1;
+ }
+ this._setAnchorName(anchorName + n);
+ }
+ return `#${this.$target[0].id}`;
+ },
+ /**
+ * Creates a safe id/anchor from text.
+ *
+ * @private
+ * @param {string} text
+ * @returns {string}
+ */
+ _text2Anchor: function (text) {
+ return encodeURIComponent(text.trim().replace(/\s+/g, '-'));
+ },
});
/**
diff --git a/addons/website/static/src/xml/website.editor.xml b/addons/website/static/src/xml/website.editor.xml
index 9f8a2e302fc..31ffb28704d 100644
--- a/addons/website/static/src/xml/website.editor.xml
+++ b/addons/website/static/src/xml/website.editor.xml
@@ -115,7 +115,6 @@
The chosen name is not valid (use only a-Z A-Z 0-9 - _)
The chosen name already exists