From 796ffe8ee8277e162857c1f276e9f1e47f32375b Mon Sep 17 00:00:00 2001 From: Ivan Ternovtsiy Date: Thu, 23 Jul 2020 14:59:13 +0300 Subject: [PATCH] implement custom tag link --- .../buttons/ez-btn-customtag-update.js | 206 +++++++++++++++++- .../Resources/public/scss/_custom-tag.scss | 26 +++ .../public/scss/variables/colors.scss | 5 + .../translations/alloy_editor.en.xliff | 10 + 4 files changed, 245 insertions(+), 2 deletions(-) diff --git a/src/bundle/Resources/public/js/OnlineEditor/buttons/ez-btn-customtag-update.js b/src/bundle/Resources/public/js/OnlineEditor/buttons/ez-btn-customtag-update.js index 5e36e199..4d6ef2a4 100644 --- a/src/bundle/Resources/public/js/OnlineEditor/buttons/ez-btn-customtag-update.js +++ b/src/bundle/Resources/public/js/OnlineEditor/buttons/ez-btn-customtag-update.js @@ -13,6 +13,7 @@ export default class EzBtnCustomTagUpdate extends EzWidgetButton { this.state = { values: props.values, + linkDetails: {}, }; } @@ -136,8 +137,209 @@ export default class EzBtnCustomTagUpdate extends EzWidgetButton { * @return {Object} The rendered link. */ renderLink(config, attrName) { - // @todo provide dedicated support for link attribute type - return this.renderString(config, attrName); + const selectContentLabel = Translator.trans( + /*@Desc("Select content")*/ 'custom_tag.link.select_content_btn.label', + {}, + 'alloy_editor' + ); + if (this.state.values[attrName].value) { + this.loadLinkContentInfo(attrName); + } + const linkDetails = this.state.linkDetails[attrName]; + + return ( +
+ + +
+ {linkDetails + ? {linkDetails.title} + : '' + } + +
+
+ ); + } + + updateLinkValues(event) { + this.updateValues(event); + this.loadLinkContentInfo(event.target.dataset.attrName); + } + + loadLinkContentInfo(attrName) { + const inputValue = this.state.values[attrName].value; + const linkDetails = this.state.linkDetails[attrName] || {}; + + let filter; + if (inputValue.startsWith('ezlocation://') && inputValue.length > 13) { + const locationId = parseInt(inputValue.substring(13)); + if (!locationId) { + this.clearLinkDetails(attrName); + return; + } + if (linkDetails.locationId === locationId) { + return; + } + linkDetails.locationId = locationId; + filter = { LocationIdCriterion: locationId }; + } else if (inputValue.startsWith('ezcontent://') && inputValue.length > 14) { + const contentId = parseInt(inputValue.substring(14)); + if (!contentId) { + this.clearLinkDetails(attrName); + return; + } + if (linkDetails.contentId === contentId) { + return; + } + linkDetails.contentId = contentId; + filter = { ContentIdCriterion: contentId }; + } else { + this.clearLinkDetails(attrName); + return; + } + + const token = document.querySelector('meta[name="CSRF-Token"]').content; + const siteaccess = document.querySelector('meta[name="SiteAccess"]').content; + + const body = JSON.stringify({ + ViewInput: { + identifier: `custom-tag-link-info-by-id-${linkDetails.contentId || ''}-${linkDetails.locationId || ''}`, + public: false, + LocationQuery: { + Criteria: {}, + FacetBuilders: {}, + SortClauses: { LocationDepth: 'ascending' }, + Filter: filter, + limit: 1, + offset: 0, + }, + }, + }); + const request = new Request('/api/ezp/v2/views', { + method: 'POST', + headers: { + Accept: 'application/vnd.ez.api.View+json; version=1.1', + 'Content-Type': 'application/vnd.ez.api.ViewInput+json; version=1.1', + 'X-Requested-With': 'XMLHttpRequest', + 'X-Siteaccess': siteaccess, + 'X-CSRF-Token': token, + }, + body, + mode: 'same-origin', + credentials: 'same-origin', + }); + + fetch(request) + .then(window.eZ.helpers.request.getJsonFromResponse) + .then((viewData) => { + const resHits = viewData.View.Result.searchHits.searchHit; + if (!resHits.length || !resHits[0].value) { + this.clearLinkDetails(attrName) + return; + } + + this.setLinkDetails(attrName, resHits[0].value.Location); + }); + } + + setLinkDetails(attrName, location) { + const content = location.ContentInfo.Content; + const linkDetails = Object.assign({}, this.state.linkDetails); + linkDetails[attrName] = { + title: content.TranslatedName || content.Name || '', + href: Routing.generate('_ez_content_view', { + contentId: content._id, + locationId: location.id, + }), + contentId: content._id, + locationId: location.id, + }; + this.setState({ linkDetails }); + } + + clearLinkDetails(attrName) { + if (this.state.linkDetails[attrName]) { + const linkDetails = Object.assign({}, this.state.linkDetails); + delete linkDetails[attrName]; + this.setState({ linkDetails }); + } + } + + /** + * Runs the Universal Discovery Widget so that the user can pick a Content. + * + * @method selectContent + * @protected + */ + selectContent(attrName) { + const openUDW = () => { + const config = JSON.parse(document.querySelector(`[data-udw-config-name="richtext_embed"]`).dataset.udwConfig); + const title = Translator.trans(/*@Desc("Select content")*/ 'custom_tag.link.udw.title', {}, 'alloy_editor'); + const selectContent = eZ.richText.alloyEditor.callbacks.selectContent; + const mergedConfig = Object.assign( + { + onConfirm: this.udwOnConfirm.bind(this, attrName), + onCancel: this.udwOnCancel.bind(this), + title, + multiple: false, + }, + config + ); + + if (typeof selectContent === 'function') { + selectContent(mergedConfig); + } + }; + openUDW(); + + this.disableUDWPropagation(); + } + + udwOnConfirm(attrName, items) { + this.state.values[attrName].value = 'ezlocation://' + items[0].id; + this.setLinkDetails(attrName, items[0]); + + this.enableUDWPropagation(); + } + + udwOnCancel() { + ReactDOM.unmountComponentAtNode(document.querySelector('#react-udw')); + this.enableUDWPropagation(); + } + + /** + * Disable propagation to make sure attributes toolbar + * not closed by alloyeditor outside click + */ + disableUDWPropagation() { + const container = document.querySelector('body'); + container.addEventListener('mousedown', this.doNotPropagate); + container.addEventListener('keydown', this.doNotPropagate); + } + + enableUDWPropagation() { + const container = document.querySelector('body'); + container.removeEventListener('mousedown', this.doNotPropagate); + container.removeEventListener('keydown', this.doNotPropagate); + } + + doNotPropagate(event) { + event.stopPropagation(); } /** diff --git a/src/bundle/Resources/public/scss/_custom-tag.scss b/src/bundle/Resources/public/scss/_custom-tag.scss index b48a070f..56dc128e 100644 --- a/src/bundle/Resources/public/scss/_custom-tag.scss +++ b/src/bundle/Resources/public/scss/_custom-tag.scss @@ -46,6 +46,32 @@ align-items: center; } + &__link-controls { + .btn.btn-secondary { + width: 100%; + margin: calculateRem(5px) calculateRem(5px) calculateRem(5px) 0; + color: $ez-white; + } + .ez-custom-tag--link { + display: block; + width: 100%; + padding: calculateRem(10px) calculateRem(5px) calculateRem(5px) calculateRem(5px); + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + color: $ez-color-hyperlink; + text-decoration: none; + cursor: pointer; + transition: color 0.3s $ez-admin-transition; + + &:hover, + &:focus { + color: $ez-color-hyperlink-hover; + text-decoration: underline; + } + } + } + & > [data-ezelement='ezattributes'], & > [data-ezelement='ezcontent'] { display: none; diff --git a/src/bundle/Resources/public/scss/variables/colors.scss b/src/bundle/Resources/public/scss/variables/colors.scss index b1747bcb..48fd030f 100644 --- a/src/bundle/Resources/public/scss/variables/colors.scss +++ b/src/bundle/Resources/public/scss/variables/colors.scss @@ -13,6 +13,11 @@ $ez-ground-base: #f3f3f3; $ez-ground-base-pale: #fafafa; $ez-ground-base-dark: #e5e3e3; $ez-secondary-ground: #2b84b1; +$ez-color-hyperlink: #0645ad; +$ez-color-hyperlink-hover: darken($ez-color-hyperlink, 15%); + +// eZ transition function +$ez-admin-transition: cubic-bezier(0.25, 0.8, 0.25, 1); // AlloyEditor $ez-ae-color-primary: $ez-white !default; diff --git a/src/bundle/Resources/translations/alloy_editor.en.xliff b/src/bundle/Resources/translations/alloy_editor.en.xliff index d4cee13f..2ffad955 100644 --- a/src/bundle/Resources/translations/alloy_editor.en.xliff +++ b/src/bundle/Resources/translations/alloy_editor.en.xliff @@ -71,6 +71,16 @@ Save key: custom_tag_update_btn.save_btn.label + + Select content + Select content + key: custom_tag.link.select_content_btn.label + + + Select content + Select content + key: custom_tag.link.udw.title + This embedded item relies on 'ezlocation' imported from Legacy. It isn't supported by Online Editor yet. This embedded item relies on 'ezlocation' imported from Legacy. It isn't supported by Online Editor yet.