Goal
AEM Cloud Version : 2021.3.5026.20210309T210727Z-210225 (March 09, 2021)
This post explains adding a plugin to RTE (Rich Text Editor - /libs/cq/gui/components/authoring/dialog/richtext) for adding custom CSS and color to text...
Package install contains a Text component with design dialog with plugin configuration - /apps/eaem-cs-rte-plugin-color-picker/components/text/cq:design_dialog/content/items/tabs/items/plugins/items/eaem-aem-fonts
Demo | Package Install | Github
Plugin Picker
Applying Color
Design Dialog Configuration
Enable Plugin Component Policy
Plugin Config saved in Component Policy
Solution
1) Create the Picker configuration dialog /apps/eaem-cs-rte-plugin-color-picker/clientlibs/fonts-plugin/font-selector and add categories=eaem.rte.font.plugin in /apps/eaem-cs-rte-plugin-color-picker/clientlibs/fonts-plugin/font-selector/jcr:content/head/clientlibs
<?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 Font Selector" 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.font.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="Select Text Font Color..." sling:resourceType="granite/ui/components/coral/foundation/wizard"> <items jcr:primaryType="nt:unstructured"> <text jcr:primaryType="nt:unstructured" jcr:title="Select Text Font Color..." sling:resourceType="granite/ui/components/coral/foundation/container"> <items jcr:primaryType="nt:unstructured"> <fixedColumns 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"> <contents jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/select" fieldDescription="Appply specific background style..." fieldLabel="Background Style" name="./style"> <items jcr:primaryType="nt:unstructured"> <none jcr:primaryType="nt:unstructured" text="None" value=""/> <gray jcr:primaryType="nt:unstructured" text="Gray" value="eaem--background-gray"/> <white jcr:primaryType="nt:unstructured" text="White" value="eaem--background-white"/> <black jcr:primaryType="nt:unstructured" text="Black" value="eaem--background-black"/> </items> </contents> <hideOnDesktop jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/checkbox" fieldDescription="Hide the text on desktop ( >= 992px)" name="./hideOnDesktop" text="Hide on desktop" value="true"/> <hideOnTablet jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/checkbox" fieldDescription="Hide the text on tablet (768 - 991.95 px)" name="./hideOnTablet" text="Hide on Tablet" value="true"/> <hideOnMobile jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/checkbox" fieldDescription="Hide the text on mobile breakpoints (< 768px)" name="./hideOnMobile" text="Hide on mobile" value="true"/> <color jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/colorfield" fieldDescription="Select text color" fieldLabel="Text Color" name="./color" showProperties="{Boolean}true"/> <bgColor jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/colorfield" fieldDescription="Select background color" fieldLabel="Background Color" name="./bgColor" showProperties="{Boolean}true"/> </items> </column> </items> </fixedColumns> </items> <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="Apply" type="submit" variant="primary"> <granite:data jcr:primaryType="nt:unstructured" foundation-wizard-control-action="next"/> </next> </parentConfig> </text> </items> </wizard> </items> </form> </items> </body> </jcr:content> </jcr:root>
2) Add any custom styles for the author user to (pick and apply on text using the plugin) in clientlib base eg. /apps/eaem-cs-rte-plugin-color-picker/clientlibs/clientlib-base/rte-fonts.css
.eaem--background-gray{ background-color: #f1f1f1; } .eaem--background-white{ background-color: #ffffff; } .eaem--background-black{ background-color: #000000; } @media (min-width: 992px){ .eaem--content-desktop-hide { display: none !important; } } @media (min-width:768px) and (max-width:991.95px){ .eaem--content-tablet-hide { display: none !important; } } @media (max-width: 767.95px){ .eaem--content-mobile-hide { display: none !important; } }
3) Add the plugin execution logic in /apps/eaem-cs-rte-plugin-color-picker/clientlibs/fonts-plugin/clientlib/rte-fonts-plugin.js with categories=[cq.authoring.dialog.all, eaem.rte.font.plugin]
(function($, CUI, $document){ var GROUP = "eaem-aem-fonts", FONT_FEATURE = "applyFont", TEXT_COLOR_FEATURE = "textColor", TEXT_BG_COLOR_FEATURE = "textBackgroundColor", EAEM_APPLY_FONT_DIALOG = "eaemTouchUIApplyFontDialog", SENDER = "eaem-aem", REQUESTER = "requester", $eaemFontPicker, CANCEL_CSS = "[data-foundation-wizard-control-action='cancel']", FONT_SELECTOR_URL = "/apps/eaem-cs-rte-plugin-color-picker/clientlibs/fonts-plugin/font-selector.html", MOBILE_HIDE_CONTENT_CLASS = "eaem--content-mobile-hide", DESKTOP_HIDE_CONTENT_CLASS = "eaem--content-desktop-hide", TABLET_HIDE_CONTENT_CLASS = "eaem--content-tablet-hide", url = document.location.pathname; if( url.indexOf(FONT_SELECTOR_URL) == 0 ){ handlePicker(); return; } function handlePicker(){ $document.on("foundation-contentloaded", fillDefaultValues); $document.on("click", CANCEL_CSS, sendCancelMessage); $document.submit(sentTextAttributes); } 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 setWidgetValue(form, selector, value, enable){ Coral.commons.ready(form.querySelector(selector), function (field) { if(field.tagName == "CORAL-CHECKBOX"){ if(value == "true"){ field.checked = true; } }else{ field.value = _.isEmpty(value) ? "" : decodeURIComponent(value); } if(enable){ delete field.disabled; }else{ field.disabled = "disabled"; } }); } function fillDefaultValues(){ var queryParams = queryParameters(), $form = $("form"); if(_.isEmpty(queryParams.features)){ return; } var features = queryParams.features.split(","); setWidgetValue($form[0], "[name='./style']", queryParams.class, true); setWidgetValue($form[0], "[name='./hideOnMobile']", queryParams.hideOnMobile, true); setWidgetValue($form[0], "[name='./hideOnTablet']", queryParams.hideOnTablet, true); setWidgetValue($form[0], "[name='./hideOnDesktop']", queryParams.hideOnDesktop, true); setWidgetValue($form[0], "[name='./color']", queryParams.color, features.includes(TEXT_COLOR_FEATURE)); setWidgetValue($form[0], "[name='./bgColor']", queryParams.bgColor, features.includes(TEXT_BG_COLOR_FEATURE)); $form.css("background-color", "#fff"); } function sentTextAttributes(){ var message = { sender: SENDER, action: "submit", data: {} }, $form = $("form"), $field; _.each($form.find("[name^='./']"), function(field){ $field = $(field); message.data[$field.attr("name").substr(2)] = $field.val(); }); addCheckboxValue(message, $form, "./hideOnDesktop"); addCheckboxValue(message, $form, "./hideOnTablet"); addCheckboxValue(message, $form, "./hideOnMobile"); getParent().postMessage(JSON.stringify(message), "*"); } function addCheckboxValue(message, $form, cbName){ var $checkbox = $form.find("coral-checkbox[name='" + cbName + "']"); if(!_.isEmpty($checkbox)){ message.data[$checkbox.attr("name").substr(2)] = $checkbox[0].checked; } } function sendCancelMessage(){ var message = { sender: SENDER, action: "cancel" }; getParent().postMessage(JSON.stringify(message), "*"); } function getParent() { if (window.opener) { return window.opener; } return parent; } addPlugin(); addPluginToDefaultUISettings(); addDialogTemplate(); function addDialogTemplate(){ var url = Granite.HTTP.externalize(FONT_SELECTOR_URL) + "?" + REQUESTER + "=" + SENDER; var html = "<iframe width='600px' height='450px' frameBorder='0' src='" + url + "'></iframe>"; if(_.isUndefined(CUI.rte.Templates)){ CUI.rte.Templates = {}; } if(_.isUndefined(CUI.rte.templates)){ CUI.rte.templates = {}; } try{ CUI.rte.templates['dlg-' + EAEM_APPLY_FONT_DIALOG] = CUI.rte.Templates['dlg-' + EAEM_APPLY_FONT_DIALOG] = Handlebars.compile(html); }catch(err){ console.log("Ignoring font plugin error", err); } } function rgbToHex(color){ if(_.isEmpty(color)){ return color; } if(color.indexOf("rgb") == 0){ color = CUI.util.color.RGBAToHex(color); } return color; } function addPluginToDefaultUISettings(){ var groupFeature = GROUP + "#" + FONT_FEATURE, toolbar = CUI.rte.ui.cui.DEFAULT_UI_SETTINGS.dialogFullScreen.toolbar; if(toolbar.includes(groupFeature)){ return; } toolbar.splice(3, 0, groupFeature); } var EAEMApplyFontDialog = new Class({ extend: CUI.rte.ui.cui.AbstractDialog, toString: "EAEMApplyFontDialog", initialize: function(config) { this.exec = config.execute; }, getDataType: function() { return EAEM_APPLY_FONT_DIALOG; } }); function addPlugin(){ var EAEMTouchUIFontPlugin = new Class({ toString: "EAEMTouchUIFontPlugin", extend: CUI.rte.plugins.Plugin, pickerUI: null, getFeatures: function() { return [ FONT_FEATURE ]; }, initializeUI: function(tbGenerator) { var plg = CUI.rte.plugins; addPluginToDefaultUISettings(); if (!this.isFeatureEnabled(FONT_FEATURE)) { return; } this.pickerUI = tbGenerator.createElement(FONT_FEATURE, this, false, { title: "Select Font..." }); tbGenerator.addElement(GROUP, plg.Plugin.SORT_FORMAT, this.pickerUI, 10); var groupFeature = GROUP + "#" + FONT_FEATURE; tbGenerator.registerIcon(groupFeature, "colorPalette"); }, notifyPluginConfig: function (pluginConfig) { pluginConfig = pluginConfig || {}; CUI.rte.Utils.applyDefaults(pluginConfig, { 'tooltips': { applyFont: { 'title': 'Apply Font', 'text': 'Apply Font to selected text' } } }); this.config = pluginConfig; }, execute: function (pluginCommand, value, envOptions) { var context = envOptions.editContext, ek = this.editorKernel; if (pluginCommand != FONT_FEATURE) { return; } if(!isValidSelection()){ return; } var 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")), clazz = $tag.attr("class"), hideOnMobile = false, hideOnDesktop = false, hideOnTablet = false, size = $tag.css("font-size"),dialog,dm = ek.getDialogManager(), $container = CUI.rte.UIUtils.getUIContainer($(context.root)), propConfig = { 'parameters': { 'command': this.pluginId + '#' + FONT_FEATURE } }; if(clazz && clazz.includes(MOBILE_HIDE_CONTENT_CLASS)){ hideOnMobile = true; clazz = clazz.replace(MOBILE_HIDE_CONTENT_CLASS, "").trim(); } if(clazz && clazz.includes(TABLET_HIDE_CONTENT_CLASS)){ hideOnTablet = true; clazz = clazz.replace(TABLET_HIDE_CONTENT_CLASS, "").trim(); } if(clazz && clazz.includes(DESKTOP_HIDE_CONTENT_CLASS)){ hideOnDesktop = true; clazz = clazz.replace(DESKTOP_HIDE_CONTENT_CLASS, "").trim(); } var color = this.getColorAttributes($tag); if(this.eaemApplyFontDialog){ dialog = this.eaemApplyFontDialog; dialog.$dialog.find("iframe").attr("src", this.getPickerIFrameUrl(this.config.features, size, clazz, hideOnMobile, hideOnTablet, hideOnDesktop, color.color, color.bgColor)); }else{ dialog = new EAEMApplyFontDialog(); dialog.attach(propConfig, $container, this.editorKernel); dialog.$dialog.css("-webkit-transform", "scale(0.9)").css("-webkit-transform-origin", "0 0") .css("-moz-transform", "scale(0.9)").css("-moz-transform-origin", "0px 0px"); dialog.$dialog.find("iframe").attr("src", this.getPickerIFrameUrl(this.config.features, size, clazz, hideOnMobile, hideOnTablet, hideOnDesktop, color.color, color.bgColor)); this.eaemApplyFontDialog = dialog; } dm.show(dialog); $(window).off('message', receiveMessage).on('message', receiveMessage); function isValidSelection(){ var winSel = window.getSelection(); return winSel && winSel.rangeCount == 1 && winSel.getRangeAt(0).toString().length > 0; } function receiveMessage(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 !== SENDER) { return; } action = message.action; if(action === "submit"){ ek.relayCmd(pluginCommand, message.data); } dialog.hide(); } }, getColorAttributes: function($tag){ var key, color = { color: "", bgColor : ""}; if(!$tag.attr("style")){ return color; } //donot use .css("color"), it returns default font color, if color is not set var parts = $tag.attr("style").split(";"); _.each(parts, function(value){ value = value.split(":"); key = value[0] ? value[0].trim() : ""; value = value[1] ? value[1].trim() : ""; if(key == "color"){ color.color = rgbToHex(value); }else if(key == "background-color"){ color.bgColor = rgbToHex(value); } }); return color; }, showFontModal: function(url){ var self = this, $iframe = $('<iframe>'), $modal = $('<div>').addClass('eaem-cfm-font-size coral-Modal'); $iframe.attr('src', url).appendTo($modal); $modal.appendTo('body').modal({ type: 'default', buttons: [], visible: true }); $eaemFontPicker = $modal; $eaemFontPicker.eaemFontPlugin = self; $modal.nextAll(".coral-Modal-backdrop").addClass("cfm-coral2-backdrop"); }, getPickerIFrameUrl: function(features, size, clazz, hideOnMobile, hideOnTablet, hideOnDesktop, color, bgColor){ var url = Granite.HTTP.externalize(FONT_SELECTOR_URL) + "?" + REQUESTER + "=" + SENDER; if(features === "*"){ features = [TEXT_COLOR_FEATURE , TEXT_BG_COLOR_FEATURE]; } url = url + "&features=" + features.join(","); if(!_.isEmpty(color)){ url = url + "&color=" + encodeURIComponent(color); } if(!_.isEmpty(bgColor)){ url = url + "&bgColor=" + encodeURIComponent(bgColor); } if(!_.isEmpty(size)){ url = url + "&size=" + size; } if(!_.isEmpty(clazz)){ url = url + "&class=" + clazz; } if(hideOnMobile){ url = url + "&hideOnMobile=" + hideOnMobile; } if(hideOnTablet){ url = url + "&hideOnTablet=" + hideOnTablet; } if(hideOnDesktop){ url = url + "&hideOnDesktop=" + hideOnDesktop; } return url; }, updateState: function(selDef) { var hasUC = this.editorKernel.queryState(FONT_FEATURE, selDef); if (this.pickerUI != null) { this.pickerUI.setSelected(hasUC); } } }); var EAEMTouchUIFontCmd = new Class({ toString: "EAEMTouchUIFontCmd", extend: CUI.rte.commands.Command, isCommand: function (cmdStr) { return (cmdStr.toLowerCase() == FONT_FEATURE); }, getProcessingOptions: function () { var cmd = CUI.rte.commands.Command; return cmd.PO_SELECTION | cmd.PO_BOOKMARK | cmd.PO_NODELIST; }, getTagObject: function(textData) { var style = ""; if(!_.isEmpty(textData.color)){ style = "color: " + textData.color + ";"; } if(!_.isEmpty(textData.size)){ style = style + "font-size: " + textData.size + ";"; } if(!_.isEmpty(textData.bgColor)){ style = style + "background-color: " + textData.bgColor; } var spanTag = { "tag": "span", "attributes": { "style" : style } }; var clazz = textData.style; if(!_.isEmpty(clazz)){ spanTag.attributes.class = clazz; } if(textData.hideOnMobile){ addClazz(spanTag, MOBILE_HIDE_CONTENT_CLASS); } if(textData.hideOnTablet){ addClazz(spanTag, TABLET_HIDE_CONTENT_CLASS); } if(textData.hideOnDesktop){ addClazz(spanTag, DESKTOP_HIDE_CONTENT_CLASS); } return spanTag; function addClazz(tag, tagClazz){ tag.attributes.class = tag.attributes.class ? (tag.attributes.class + " ") : ""; tag.attributes.class = tag.attributes.class + tagClazz; } }, execute: function (execDef) { var textData = execDef.value, selection = execDef.selection, nodeList = execDef.nodeList; if (!selection || !nodeList) { return; } var common = CUI.rte.Common, context = execDef.editContext, tagObj = this.getTagObject(textData); if(_.isEmpty(textData.size) && _.isEmpty(textData.color) && _.isEmpty(textData.bgColor) && _.isEmpty(textData.style) && !textData.hideOnMobile && !textData.hideOnDesktop && !textData.hideOnTablet){ nodeList.removeNodesByTag(execDef.editContext, tagObj.tag, undefined, true); return; } var tags = common.getTagInPath(context, selection.startNode, tagObj.tag); //remove existing color before adding new color if (tags != null) { nodeList.removeNodesByTag(execDef.editContext, tagObj.tag, tags.attributes ? tags.attributes : undefined, true); } nodeList.surround(execDef.editContext, tagObj.tag, tagObj.attributes); }, queryState: function(selectionDef, cmd) { return false; } }); CUI.rte.commands.CommandRegistry.register(FONT_FEATURE, EAEMTouchUIFontCmd); CUI.rte.plugins.PluginRegistry.register(GROUP,EAEMTouchUIFontPlugin); } }(jQuery, window.CUI,jQuery(document)));
No comments:
Post a Comment