Quantcast
Channel: Experiencing Adobe Experience Manager (AEM, CQ)
Viewing all articles
Browse latest Browse all 525

AEM 65 - Touch UI RTE (Rich Text Editor) Plugin for Creating Structured Content e.g. Creating Tooltips

$
0
0

Goal


Create a Touch UI RTE (Rich Text Editor) plugin for entering Structured Content, converted into HTML and added in RTE based on required functionality

With the solution discussed in this post, author can enter content in a form opened from RTE, convert into HTML, add as a tooltip for selected text

Demo | Package Install | Github


Plugin Configuration

                               Add node experience-aem under rtePlugins and set features=* 




                               Add experience-aem#structuredContentModal to uiSettings> cui > dialogFullScreen for showing the ellipsis toolbar icon in full screen



RTE Toolbar Icon




Content Form (for Tooltip) in Full Screen


Tooltip applied to text




Tooltip content in CRX



Solution


1) Login to CRXDE Lite, add nt:folder /apps/eaem-touchui-rte-structured-content

2) To show the tooltip form on plugin icon click, create /apps/eaem-touchui-rte-structured-content/structured-content of type cq:Page (line 19, check the clientlib category eaem.rte.structured.content.plugin)

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="cq:Page">
<jcr:content
jcr:mixinTypes="[sling:VanityPath]"
jcr:primaryType="nt:unstructured"
jcr:title="Experience AEM Structured Content"
sling:resourceType="granite/ui/components/coral/foundation/page">
<head jcr:primaryType="nt:unstructured">
<favicon
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/page/favicon"/>
<viewport
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/admin/page/viewport"/>
<clientlibs
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/includeclientlibs"
categories="[coralui3,granite.ui.coral.foundation,granite.ui.shell,dam.gui.admin.coral,eaem.rte.structured.content.plugin]"/>
</head>
<body
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/page/body">
<items jcr:primaryType="nt:unstructured">
<form
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form"
class="foundation-form content-container"
maximized="{Boolean}true"
style="vertical">
<items jcr:primaryType="nt:unstructured">
<wizard
jcr:primaryType="nt:unstructured"
jcr:title="Add tooltip content..."
sling:resourceType="granite/ui/components/coral/foundation/wizard">
<items jcr:primaryType="nt:unstructured">
<tooltip
jcr:primaryType="nt:unstructured"
jcr:title="Tooltip"
sling:resourceType="granite/ui/components/coral/foundation/container"
margin="{Boolean}true">
<items jcr:primaryType="nt:unstructured">
<columns
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
margin="{Boolean}true">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<title
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldDescription="Enter Title"
fieldLabel="Title"
name="title"/>
<description
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
fieldDescription="Enter Description"
fieldLabel="Description"
name="description"/>
</items>
</column>
</items>
</columns>
</items>
</tooltip>
<parentConfig jcr:primaryType="nt:unstructured">
<prev
granite:class="foundation-wizard-control"
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/anchorbutton"
text="Cancel">
<granite:data
jcr:primaryType="nt:unstructured"
foundation-wizard-control-action="cancel"/>
</prev>
<next
granite:class="foundation-wizard-control"
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/button"
disabled="{Boolean}true"
text="Create tooltip"
type="submit"
variant="primary">
<granite:data
jcr:primaryType="nt:unstructured"
foundation-wizard-control-action="next"/>
</next>
</parentConfig>
</items>
</wizard>
</items>
</form>
</items>
</body>
</jcr:content>
</jcr:root>




3) Create clientlib (cq:ClientLibraryFolder) /apps/eaem-touchui-dialog-rte-color-picker/clientlib set property categories to [cq.authoring.dialog.all, eaem.rte.structured.content.plugin] and dependencies to [lodash]

4) Create file (nt:file) /apps/eaem-touchui-dialog-rte-color-picker/clientlib/css.txt, add the following content

                                    rte-structured-content.css


5) Create file (nt:file) /apps/eaem-touchui-dialog-rte-color-picker/clientlib/rte-structured-content.css, add the following code

.eaem-rte-structured-dialog {
width: 80%;
margin-left: -20%;
height: 83%;
margin-top: -20%;
box-sizing: content-box;
z-index: 10100;
}

.eaem-rte-structured-dialog > iframe {
width: 100%;
height: 100%;
border: 1px solid #888;
}


6) Create file (nt:file) /apps/eaem-touchui-dialog-rte-color-picker/clientlib/js.txt, add the following content

                                    rte-structured-content.js


7) Create file (nt:file) /apps/eaem-touchui-dialog-rte-color-picker/clientlib/rte-structured-content.js, add the following code. #25 function getHtmlFromContent() converts the form content into tooltip html and adds it for selected text. Logic in this function can be adjusted accordingly, to create HTML from form content and add in RTE

(function($, CUI, $document){
var GROUP = "experience-aem",
STRUCTURED_CONTENT_FEATURE = "structuredContentModal",
TCP_DIALOG = "eaemTouchUIStructuredContentModalDialog",
CONTENT_IN_DIALOG = "content",
REQUESTER = "requester",
CANCEL_CSS = "[data-foundation-wizard-control-action='cancel']",
MODAL_URL = "/apps/eaem-touchui-rte-structured-content/structured-content.html",
$eaemStructuredModal, url = document.location.pathname;

if( url.indexOf(MODAL_URL) !== 0 ){
addPluginToDefaultUISettings();

addDialogTemplate();

addPlugin();
}else{
$document.on("foundation-contentloaded", fillDefaultValues);

$document.on("click", CANCEL_CSS, sendCancelMessage);

$document.submit(sendTextAttributes);
}

function getHtmlFromContent(selectedText, content){
var tooltipText = content.title + " : " + content.description;

return "<span title='" + tooltipText + "' class='eaem-dotted-underline' data-content='" + JSON.stringify(content) + "'>" +
selectedText +
"</span>";
}

function setWidgetValue(form, selector, value){
Coral.commons.ready(form.querySelector(selector), function (field) {
field.value = _.isEmpty(value) ? "" : decodeURIComponent(value);
});
}

function queryParameters() {
var result = {}, param,
params = document.location.search.split(/\?|\&/);

params.forEach( function(it) {
if (_.isEmpty(it)) {
return;
}

param = it.split("=");
result[param[0]] = param[1];
});

return result;
}

function sendTextAttributes(){
var message = {
sender: GROUP,
action: "submit",
data: {}
}, $form = $("form"), $field;

_.each($form.find("[name]"), function(field){
$field = $(field);
message.data[$field.attr("name")] = $field.val();
});

parent.postMessage(JSON.stringify(message), "*");
}

function fillDefaultValues(){
var queryParams = queryParameters(),
form = $("form")[0];

if(_.isEmpty(queryParams[CONTENT_IN_DIALOG])){
return;
}

var content = JSON.parse(decodeURIComponent(queryParams[CONTENT_IN_DIALOG]));

_.each(content, function(value, key){
setWidgetValue(form, "[name='" + key + "']", value);
});
}

function sendCancelMessage(){
var message = {
sender: GROUP,
action: "cancel"
};

getParent().postMessage(JSON.stringify(message), "*");
}

function getParent() {
if (window.opener) {
return window.opener;
}

return parent;
}

function closeDialogModal(event){
event = event.originalEvent || {};

if (_.isEmpty(event.data)) {
return;
}

var message, action;

try{
message = JSON.parse(event.data);
}catch(err){
return;
}

if (!message || message.sender !== GROUP) {
return;
}

action = message.action;

if(action === "submit"){
var ek = $eaemStructuredModal.eaemModalPlugin.editorKernel,
tooltipHtml = getHtmlFromContent(window.getSelection().toString(), message.data);

ek.execCmd('inserthtml', tooltipHtml);

ek.focus();
}

var modal = $eaemStructuredModal.data('modal');
modal.hide();
modal.$element.remove();
}

function addPluginToDefaultUISettings(){
var toolbar = CUI.rte.ui.cui.DEFAULT_UI_SETTINGS.inline.toolbar;
toolbar.splice(3, 0, GROUP + "#" + STRUCTURED_CONTENT_FEATURE);

toolbar = CUI.rte.ui.cui.DEFAULT_UI_SETTINGS.fullscreen.toolbar;
toolbar.splice(3, 0, GROUP + "#" + STRUCTURED_CONTENT_FEATURE);
}

function addDialogTemplate(){
var url = MODAL_URL + "?" + REQUESTER + "=" + GROUP;

var html = "<iframe width='600px' height='500px' frameBorder='0' src='" + url + "'></iframe>";

if(_.isUndefined(CUI.rte.Templates)){
CUI.rte.Templates = {};
}

if(_.isUndefined(CUI.rte.templates)){
CUI.rte.templates = {};
}

CUI.rte.templates['dlg-' + TCP_DIALOG] = CUI.rte.Templates['dlg-' + TCP_DIALOG] = Handlebars.compile(html);
}

function addPlugin(){
var TouchUIStructuredContentModalPlugin = new Class({
toString: "TouchUIStructuredContentModalPlugin",

extend: CUI.rte.plugins.Plugin,

modalUI: null,

getFeatures: function() {
return [ STRUCTURED_CONTENT_FEATURE ];
},

initializeUI: function(tbGenerator) {
var plg = CUI.rte.plugins;

if (!this.isFeatureEnabled(STRUCTURED_CONTENT_FEATURE)) {
return;
}

this.modalUI = tbGenerator.createElement(STRUCTURED_CONTENT_FEATURE, this, false, { title: "Add tooltip" });
tbGenerator.addElement(GROUP, plg.Plugin.SORT_FORMAT, this.modalUI, 10);

var groupFeature = GROUP + "#" + STRUCTURED_CONTENT_FEATURE;
tbGenerator.registerIcon(groupFeature, "more");

$(window).off('message', closeDialogModal).on('message', closeDialogModal);
},

execute: function (id, value, envOptions) {
if(!isValidSelection()){
return;
}

var context = envOptions.editContext,
selection = CUI.rte.Selection.createProcessingSelection(context),
startNode = selection.startNode;

if ( (selection.startOffset === startNode.length) && (startNode != selection.endNode)) {
startNode = startNode.nextSibling;
}

var tag = CUI.rte.Common.getTagInPath(context, startNode, "span"), plugin = this, dialog,
content = $(tag).data("content");

this.showDialogModal(getModalIFrameUrl(content));

function isValidSelection(){
var winSel = window.getSelection();
return winSel && winSel.rangeCount == 1 && winSel.getRangeAt(0).toString().length > 0;
}

function getModalIFrameUrl(content){
var url = MODAL_URL + "?" + REQUESTER + "=" + GROUP;

if(_.isObject(content)){
url = url + "&" + CONTENT_IN_DIALOG + "=" + JSON.stringify(content);
}

return url;
}
},

showDialogModal: function(url){
var self = this, $iframe = $('<iframe>'),
$modal = $('<div>').addClass('eaem-rte-structured-dialog coral-Modal');

$iframe.attr('src', url).appendTo($modal);

$modal.appendTo('body').modal({
type: 'default',
buttons: [],
visible: true
});

$eaemStructuredModal = $modal;

$eaemStructuredModal.eaemModalPlugin = self;

$modal.nextAll(".coral-Modal-backdrop").addClass("cfm-coral2-backdrop");
},

//to mark the icon selected/deselected
updateState: function(selDef) {
var hasUC = this.editorKernel.queryState(STRUCTURED_CONTENT_FEATURE, selDef);

if (this.modalUI != null) {
this.modalUI.setSelected(hasUC);
}
}
});

CUI.rte.plugins.PluginRegistry.register(GROUP,TouchUIStructuredContentModalPlugin);
}
}(jQuery, window.CUI,jQuery(document)));




Viewing all articles
Browse latest Browse all 525

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>