Skip to content
Snippets Groups Projects
Commit dcb533a2 authored by Jean Valverde's avatar Jean Valverde :construction_site: Committed by Jean Valverde
Browse files

Issue #3470807 by mogtofu33, sea2709: Insert Icon button is disabled in CKEditor 5

parent 8ce919bf
No related branches found
No related tags found
1 merge request!17Issue #3470807 by mogtofu33, sea2709: Insert Icon button is disabled in CKEditor 5
Pipeline #269132 passed
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.icon=t())}(self,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,n)=>{e.exports=n("dll-reference CKEditor5.dll")("./src/core.js")},"ckeditor5/src/ui.js":(e,t,n)=>{e.exports=n("dll-reference CKEditor5.dll")("./src/ui.js")},"ckeditor5/src/widget.js":(e,t,n)=>{e.exports=n("dll-reference CKEditor5.dll")("./src/widget.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function n(i){var o=t[i];if(void 0!==o)return o.exports;var c=t[i]={exports:{}};return e[i](c,c.exports,n),c.exports}n.d=(e,t)=>{for(var i in t)n.o(t,i)&&!n.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var i={};return(()=>{"use strict";n.d(i,{default:()=>a});var e=n("ckeditor5/src/core.js"),t=n("ckeditor5/src/ui.js");class o extends e.Plugin{init(){const e=this.editor,n=this.editor.config.get("icon");if(!n)return;this.dialogURL=n.dialogURL;const{openDialog:i,dialogSettings:o={}}=n;"function"==typeof i&&e.ui.componentFactory.add("icon",(n=>{const c=e.commands.get("insertIcon"),r=new t.ButtonView(n);return r.set({label:Drupal.t("Insert Icon"),icon:'<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg\n viewBox="0 0 20 20"\n version="1.1"\n id="svg1"\n sodipodi:docname="icon.svg"\n inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"\n xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"\n xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"\n xmlns="http://www.w3.org/2000/svg"\n xmlns:svg="http://www.w3.org/2000/svg">\n <defs\n id="defs1" />\n <sodipodi:namedview\n id="namedview1"\n pagecolor="#ffffff"\n bordercolor="#000000"\n borderopacity="0.25"\n inkscape:showpageshadow="2"\n inkscape:pageopacity="0.0"\n inkscape:pagecheckerboard="0"\n inkscape:deskcolor="#d1d1d1"\n inkscape:zoom="10.631582"\n inkscape:cx="-17.589104"\n inkscape:cy="13.356432"\n inkscape:window-width="1920"\n inkscape:window-height="1012"\n inkscape:window-x="0"\n inkscape:window-y="0"\n inkscape:window-maximized="1"\n inkscape:current-layer="svg1" />\n <path\n id="path1"\n d="m 2.8007812,3.0292969 c -0.6099951,0 -1.1015624,0.4707854 -1.1015624,1.0507812 v 3.0509806 c 0.7594661,7.88e-5 0.6098625,-0.1875593 1.5,-0.1876963 V 4.5 h 4.5625 C 7.7694287,3.8270555 7.7108944,3.7132665 7.7089844,3.0292969 Z m 9.9199218,0 C 12.724803,3.5346514 12.510559,3.6774178 12.505859,4.5 h 4.263672 V 14.619141 L 14.25,11.650391 c -0.245295,-0.226882 -0.623846,-0.226882 -0.869141,0 L 11.150391,13.710938 7.7890625,10.570312 C 7.5490639,10.330319 7.1701548,10.309069 6.9101562,10.539062 l -3.7109374,4.080079 v -3.38086 c -0.7899957,0.01155 -0.6633571,0.542542 -1.5,0.551312 v 4.130329 c 0,0.589993 0.4898479,1.060547 1.0898437,1.060547 H 17.179688 c 0.599993,0 1.089843,-0.470554 1.089843,-1.060547 V 4.0800781 c 0,-0.5799958 -0.48985,-1.0507812 -1.089843,-1.0507812 z"\n sodipodi:nodetypes="sscccccsccccccccccccssssssc" />\n <path\n d="M 10.008435,0.75694563 C 8.4005881,0.77794673 7.1120535,2.0758777 7.125044,3.6613611 L 7.1252566,3.864942 C 7.1243433,4.0317771 7.1915706,4.1919553 7.3117856,4.3093599 7.431237,4.427388 7.5928497,4.49454 7.761944,4.496398 8.1282485,4.474667 8.411255,4.17071 8.401981,3.8089827 8.393691,2.8547638 9.1963606,2.0875961 10.163365,2.1255013 c 0.977804,0.032954 1.753957,0.8227684 1.756556,1.7874828 0.0061,0.1683046 0.0826,0.3266226 0.211111,0.437569 0.127232,0.1105673 0.29438,0.1657463 0.463483,0.1530131 0.338454,-0.00394 0.608979,-0.2787775 0.60284,-0.6124758 l -1.3e-4,-0.047102 c -0.0048,-1.7256632 -1.439718,-3.11424657 -3.1896,-3.08651097 z"\n id="path2-7-5-3"\n sodipodi:nodetypes="cccccccccccscc"\n style="stroke-width:0.966267" />\n <path\n d="M 5.5328819,9.3164894 C 5.5108434,7.7450394 4.1824182,6.4861097 2.5602287,6.4993497 L 2.3519341,6.4996273 C 2.1812357,6.4987919 2.0173718,6.5645523 1.89729,6.6820875 1.77657,6.7988761 1.7079186,6.9568545 1.7060755,7.1221228 1.7284345,7.4801302 2.0395271,7.756628 2.4096267,7.7474398 3.385937,7.7390098 4.171143,8.5232518 4.1326916,9.4683826 4.0993096,10.42407 3.291474,11.182929 2.3044233,11.185799 c -0.1721995,0.0061 -0.334157,0.08085 -0.4476282,0.206483 -0.1130838,0.124391 -0.1694832,0.287776 -0.1563971,0.453047 0.00415,0.330794 0.285441,0.595102 0.6268638,0.588988 l 0.048193,-1.43e-4 C 4.1410723,12.428874 5.561315,11.02597 5.5323374,9.315698 Z"\n id="path2-7-5"\n sodipodi:nodetypes="cccccccccccscc"\n style="stroke-width:0.966267" />\n <path\n d="m 13.535164,5.0483665 c 0.06043,-0.1729268 0.306939,-0.1729268 0.367369,0 l 0.345918,1.0295533 c 0.154188,0.4602397 0.517536,0.8207164 0.981442,0.9736863 l 1.037753,0.3431841 c 0.174305,0.059951 0.174305,0.304514 0,0.3644671 l -1.037753,0.3431842 c -0.463077,0.1537128 -0.826504,0.5142678 -0.981442,0.9736861 l -0.345918,1.0295534 c -0.06043,0.172928 -0.306938,0.172928 -0.367369,0 L 13.189245,9.0761276 C 13.034308,8.6167093 12.670881,8.2561551 12.207804,8.1024415 L 11.171391,7.7592573 c -0.174303,-0.059951 -0.174303,-0.3045139 0,-0.3644671 l 1.037754,-0.3431841 c 0.463906,-0.1529699 0.827253,-0.5134466 0.981441,-0.9736863 z"\n id="path1-6"\n style="stroke-width:1.33546" />\n</svg>\n',tooltip:!0}),r.bind("isOn","isEnabled").to(c,"value","isEnabled"),this.listenTo(r,"execute",(()=>{i(this.dialogURL,(({settings:t})=>{e.execute("insertIcon",t)}),o)})),r}))}}var c=n("ckeditor5/src/widget.js");class r extends e.Command{execute(e){if(!e.icon)return;const t={drupalIconId:e.icon,drupalIconSettings:JSON.stringify(e.icon_settings)};this.editor.model.change((e=>{this.editor.model.insertObject(function(e,t){return e.createElement("drupalIcon",t)}(e,t))}))}refresh(){const e=this.editor.model,t=e.document.selection,n=e.schema.findAllowedParent(t.getFirstPosition(),"drupalIcon");this.isEnabled=null!==n}}class s extends e.Plugin{static get requires(){return[c.Widget]}constructor(e){super(e),this.attrs={drupalIconId:"data-icon-id",drupalIconSettings:"data-icon-settings"},this.converterAttributes=["drupalIconId","drupalIconSettings"]}init(){this._defineSchema(),this._defineConverters(),this.editor.commands.add("insertIcon",new r(this.editor))}_defineSchema(){this.editor.model.schema.register("drupalIcon",{inheritAllFrom:"imageInline",allowAttributes:Object.keys(this.attrs)}),this.editor.editing.view.domConverter.blockElements.push("drupal-icon")}_defineConverters(){const{conversion:e}=this.editor;e.for("upcast").elementToElement({model:"drupalIcon",view:{name:"drupal-icon"}}),e.for("dataDowncast").elementToElement({model:"drupalIcon",view:{name:"drupal-icon"}}),e.for("editingDowncast").elementToElement({model:"drupalIcon",view:(e,{writer:t})=>{const n=t.createContainerElement("span",{class:"drupal-icon"});return t.setCustomProperty("drupalIcon",!0,n),(0,c.toWidget)(n,t,{label:Drupal.t("Icon widget")})}}).add((e=>{const t=async(e,t,n)=>{const i=n.writer,o=t.item,c=n.mapper.toViewElement(o),r=c.getChild(0);r&&i.remove(r);const d=i.createRawElement("span",{"data-drupal-icon-preview":"loading"});i.insert(i.createPositionAt(c,0),d);try{const{preview:e}=await s._fetchIcon(o);if(!d)return;this.editor.editing.view.change((t=>{const n=t.createRawElement("span",{"data-drupal-icon-preview":"ready"},(t=>{t.innerHTML=e}));t.insert(t.createPositionBefore(d),n),t.remove(d)}))}catch(e){console.error("Error fetching icon preview:",e)}};return this.converterAttributes.forEach((n=>{e.on(`attribute:${n}:drupalIcon`,t)})),e})),Object.keys(this.attrs).forEach((t=>{const n={model:{key:t,name:"drupalIcon"},view:{name:"drupal-icon",key:this.attrs[t]}};e.for("dataDowncast").attributeToAttribute(n),e.for("upcast").attributeToAttribute(n)}))}static async _fetchIcon(e){let t=e.getAttribute("drupalIconSettings");void 0===t&&(t="");const n={icon_id:e.getAttribute("drupalIconId"),settings:t},i=await fetch(`${Drupal.url("ui-icons/icon/preview")}?${new URLSearchParams(n)}`);if(i.ok){return{preview:await i.text()}}return{preview:`<span class="drupal-icon"><svg width="15" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><title>${`The referenced icon: "${e.getAttribute("drupalIconId")}" is missing and needs to be re-embedded.`}</title><path d="M7.002 0a7 7 0 100 14 7 7 0 000-14zm3 5c0 .551-.16 1.085-.477 1.586l-.158.22c-.07.093-.189.241-.361.393a9.67 9.67 0 01-.545.447l-.203.189-.141.129-.096.17L8 8.369v.63H5.999v-.704c.026-.396.078-.73.204-.999a2.83 2.83 0 01.439-.688l.225-.21-.01-.015.176-.14.137-.128c.186-.139.357-.277.516-.417l.148-.18A.948.948 0 008.002 5 1.001 1.001 0 006 5H4a3 3 0 016.002 0zm-1.75 6.619a.627.627 0 01-.625.625h-1.25a.627.627 0 01-.626-.625v-1.238c0-.344.281-.625.626-.625h1.25c.344 0 .625.281.625.625v1.238z" fill="#d72222"/></svg></span>`}}}class d extends e.Plugin{static get requires(){return[o,s]}static get pluginName(){return"Icon"}}const a={Icon:d}})(),i=i.default})()));
\ No newline at end of file
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.icon=t())}(self,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,t,n)=>{e.exports=n("dll-reference CKEditor5.dll")("./src/core.js")},"ckeditor5/src/ui.js":(e,t,n)=>{e.exports=n("dll-reference CKEditor5.dll")("./src/ui.js")},"ckeditor5/src/widget.js":(e,t,n)=>{e.exports=n("dll-reference CKEditor5.dll")("./src/widget.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},t={};function n(i){var o=t[i];if(void 0!==o)return o.exports;var c=t[i]={exports:{}};return e[i](c,c.exports,n),c.exports}n.d=(e,t)=>{for(var i in t)n.o(t,i)&&!n.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var i={};return(()=>{"use strict";n.d(i,{default:()=>a});var e=n("ckeditor5/src/core.js"),t=n("ckeditor5/src/ui.js");class o extends e.Plugin{init(){const e=this.editor,n=this.editor.config.get("icon");if(!n)return;this.dialogURL=n.dialogURL;const{openDialog:i,dialogSettings:o={}}=n;"function"==typeof i&&e.ui.componentFactory.add("icon",(n=>{const c=e.commands.get("insertIcon"),r=new t.ButtonView(n);return r.set({label:Drupal.t("Insert Icon"),icon:'<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg\n viewBox="0 0 20 20"\n version="1.1"\n id="svg1"\n sodipodi:docname="icon.svg"\n inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"\n xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"\n xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"\n xmlns="http://www.w3.org/2000/svg"\n xmlns:svg="http://www.w3.org/2000/svg">\n <defs\n id="defs1" />\n <sodipodi:namedview\n id="namedview1"\n pagecolor="#ffffff"\n bordercolor="#000000"\n borderopacity="0.25"\n inkscape:showpageshadow="2"\n inkscape:pageopacity="0.0"\n inkscape:pagecheckerboard="0"\n inkscape:deskcolor="#d1d1d1"\n inkscape:zoom="10.631582"\n inkscape:cx="-17.589104"\n inkscape:cy="13.356432"\n inkscape:window-width="1920"\n inkscape:window-height="1012"\n inkscape:window-x="0"\n inkscape:window-y="0"\n inkscape:window-maximized="1"\n inkscape:current-layer="svg1" />\n <path\n id="path1"\n d="m 2.8007812,3.0292969 c -0.6099951,0 -1.1015624,0.4707854 -1.1015624,1.0507812 v 3.0509806 c 0.7594661,7.88e-5 0.6098625,-0.1875593 1.5,-0.1876963 V 4.5 h 4.5625 C 7.7694287,3.8270555 7.7108944,3.7132665 7.7089844,3.0292969 Z m 9.9199218,0 C 12.724803,3.5346514 12.510559,3.6774178 12.505859,4.5 h 4.263672 V 14.619141 L 14.25,11.650391 c -0.245295,-0.226882 -0.623846,-0.226882 -0.869141,0 L 11.150391,13.710938 7.7890625,10.570312 C 7.5490639,10.330319 7.1701548,10.309069 6.9101562,10.539062 l -3.7109374,4.080079 v -3.38086 c -0.7899957,0.01155 -0.6633571,0.542542 -1.5,0.551312 v 4.130329 c 0,0.589993 0.4898479,1.060547 1.0898437,1.060547 H 17.179688 c 0.599993,0 1.089843,-0.470554 1.089843,-1.060547 V 4.0800781 c 0,-0.5799958 -0.48985,-1.0507812 -1.089843,-1.0507812 z"\n sodipodi:nodetypes="sscccccsccccccccccccssssssc" />\n <path\n d="M 10.008435,0.75694563 C 8.4005881,0.77794673 7.1120535,2.0758777 7.125044,3.6613611 L 7.1252566,3.864942 C 7.1243433,4.0317771 7.1915706,4.1919553 7.3117856,4.3093599 7.431237,4.427388 7.5928497,4.49454 7.761944,4.496398 8.1282485,4.474667 8.411255,4.17071 8.401981,3.8089827 8.393691,2.8547638 9.1963606,2.0875961 10.163365,2.1255013 c 0.977804,0.032954 1.753957,0.8227684 1.756556,1.7874828 0.0061,0.1683046 0.0826,0.3266226 0.211111,0.437569 0.127232,0.1105673 0.29438,0.1657463 0.463483,0.1530131 0.338454,-0.00394 0.608979,-0.2787775 0.60284,-0.6124758 l -1.3e-4,-0.047102 c -0.0048,-1.7256632 -1.439718,-3.11424657 -3.1896,-3.08651097 z"\n id="path2-7-5-3"\n sodipodi:nodetypes="cccccccccccscc"\n style="stroke-width:0.966267" />\n <path\n d="M 5.5328819,9.3164894 C 5.5108434,7.7450394 4.1824182,6.4861097 2.5602287,6.4993497 L 2.3519341,6.4996273 C 2.1812357,6.4987919 2.0173718,6.5645523 1.89729,6.6820875 1.77657,6.7988761 1.7079186,6.9568545 1.7060755,7.1221228 1.7284345,7.4801302 2.0395271,7.756628 2.4096267,7.7474398 3.385937,7.7390098 4.171143,8.5232518 4.1326916,9.4683826 4.0993096,10.42407 3.291474,11.182929 2.3044233,11.185799 c -0.1721995,0.0061 -0.334157,0.08085 -0.4476282,0.206483 -0.1130838,0.124391 -0.1694832,0.287776 -0.1563971,0.453047 0.00415,0.330794 0.285441,0.595102 0.6268638,0.588988 l 0.048193,-1.43e-4 C 4.1410723,12.428874 5.561315,11.02597 5.5323374,9.315698 Z"\n id="path2-7-5"\n sodipodi:nodetypes="cccccccccccscc"\n style="stroke-width:0.966267" />\n <path\n d="m 13.535164,5.0483665 c 0.06043,-0.1729268 0.306939,-0.1729268 0.367369,0 l 0.345918,1.0295533 c 0.154188,0.4602397 0.517536,0.8207164 0.981442,0.9736863 l 1.037753,0.3431841 c 0.174305,0.059951 0.174305,0.304514 0,0.3644671 l -1.037753,0.3431842 c -0.463077,0.1537128 -0.826504,0.5142678 -0.981442,0.9736861 l -0.345918,1.0295534 c -0.06043,0.172928 -0.306938,0.172928 -0.367369,0 L 13.189245,9.0761276 C 13.034308,8.6167093 12.670881,8.2561551 12.207804,8.1024415 L 11.171391,7.7592573 c -0.174303,-0.059951 -0.174303,-0.3045139 0,-0.3644671 l 1.037754,-0.3431841 c 0.463906,-0.1529699 0.827253,-0.5134466 0.981441,-0.9736863 z"\n id="path1-6"\n style="stroke-width:1.33546" />\n</svg>\n',tooltip:!0}),r.bind("isOn","isEnabled").to(c,"value","isEnabled"),this.listenTo(r,"execute",(()=>{i(this.dialogURL,(({settings:t})=>{e.execute("insertIcon",t)}),o)})),r}))}}var c=n("ckeditor5/src/widget.js");class r extends e.Command{execute(e){if(!e.icon)return;const t={drupalIconId:e.icon,drupalIconSettings:JSON.stringify(e.icon_settings)};this.editor.model.change((e=>{this.editor.model.insertObject(function(e,t){return e.createElement("drupalIcon",t)}(e,t))}))}refresh(){const e=this.editor.model,t=e.document.selection,n=e.schema.findAllowedParent(t.getFirstPosition(),"drupalIcon");this.isEnabled=null!==n}}class s extends e.Plugin{static get requires(){return[c.Widget]}constructor(e){super(e),this.attrs={drupalIconId:"data-icon-id",drupalIconSettings:"data-icon-settings",class:"class",role:"role",ariaLabel:"aria-label",ariaHidden:"aria-hidden"},this.converterAttributes=["drupalIconId","drupalIconSettings"]}init(){this._defineSchema(),this._defineConverters(),this.editor.commands.add("insertIcon",new r(this.editor))}_defineSchema(){const{schema:e}=this.editor.model;e.register("drupalIcon",{isObject:!0,isInline:!0,allowWhere:"$text",allowAttributes:Object.keys(this.attrs)})}_defineConverters(){const{conversion:e}=this.editor;e.for("upcast").elementToElement({model:"drupalIcon",view:{name:"drupal-icon"}}),e.for("dataDowncast").elementToElement({model:"drupalIcon",view:{name:"drupal-icon"}}),e.for("editingDowncast").elementToElement({model:"drupalIcon",view:(e,{writer:t})=>{const n=t.createContainerElement("span",{class:"drupal-icon"});return t.setCustomProperty("drupalIcon",!0,n),(0,c.toWidget)(n,t,{label:Drupal.t("Icon widget")})}}).add((e=>{const t=async(e,t,n)=>{const i=n.writer,o=t.item,c=n.mapper.toViewElement(o),r=c.getChild(0);r&&i.remove(r);const d=i.createRawElement("span",{"data-drupal-icon-preview":"loading"});i.insert(i.createPositionAt(c,0),d);try{const{preview:e}=await s._fetchIcon(o);if(!d)return;this.editor.editing.view.change((t=>{const n=t.createRawElement("span",{"data-drupal-icon-preview":"ready"},(t=>{t.innerHTML=e}));t.insert(t.createPositionBefore(d),n),t.remove(d)}))}catch(e){console.error("Error fetching icon preview:",e)}};return this.converterAttributes.forEach((n=>{e.on(`attribute:${n}:drupalIcon`,t)})),e})),Object.keys(this.attrs).forEach((t=>{const n={model:{key:t,name:"drupalIcon"},view:{name:"drupal-icon",key:this.attrs[t]}};e.for("dataDowncast").attributeToAttribute(n),e.for("upcast").attributeToAttribute(n)}))}static async _fetchIcon(e){let t=e.getAttribute("drupalIconSettings");void 0===t&&(t="");const n={icon_id:e.getAttribute("drupalIconId"),settings:t},i=await fetch(`${Drupal.url("ui-icons/icon/preview")}?${new URLSearchParams(n)}`);if(i.ok){return{preview:await i.text()}}return{preview:`<span class="drupal-icon"><svg width="15" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><title>${`The referenced icon: "${e.getAttribute("drupalIconId")}" is missing and needs to be re-embedded.`}</title><path d="M7.002 0a7 7 0 100 14 7 7 0 000-14zm3 5c0 .551-.16 1.085-.477 1.586l-.158.22c-.07.093-.189.241-.361.393a9.67 9.67 0 01-.545.447l-.203.189-.141.129-.096.17L8 8.369v.63H5.999v-.704c.026-.396.078-.73.204-.999a2.83 2.83 0 01.439-.688l.225-.21-.01-.015.176-.14.137-.128c.186-.139.357-.277.516-.417l.148-.18A.948.948 0 008.002 5 1.001 1.001 0 006 5H4a3 3 0 016.002 0zm-1.75 6.619a.627.627 0 01-.625.625h-1.25a.627.627 0 01-.626-.625v-1.238c0-.344.281-.625.626-.625h1.25c.344 0 .625.281.625.625v1.238z" fill="#d72222"/></svg></span>`}}}class d extends e.Plugin{static get requires(){return[o,s]}static get pluginName(){return"Icon"}}const a={Icon:d}})(),i=i.default})()));
\ No newline at end of file
......@@ -25,6 +25,10 @@ export default class IconEditing extends Plugin {
this.attrs = {
drupalIconId: 'data-icon-id',
drupalIconSettings: 'data-icon-settings',
class: 'class',
role: 'role',
ariaLabel: 'aria-label',
ariaHidden: 'aria-hidden',
};
this.converterAttributes = ['drupalIconId', 'drupalIconSettings'];
}
......@@ -45,16 +49,23 @@ export default class IconEditing extends Plugin {
* @private
*/
_defineSchema() {
const schema = this.editor.model.schema;
const { schema } = this.editor.model;
schema.register('drupalIcon', {
// inheritAllFrom: '$inlineObject',
inheritAllFrom: 'imageInline',
// Behaves like a self-contained object (e.g. an image).
isObject: true,
// Allows placement of the object to be inline with text.
isInline: true,
// Allows an icon to be inserted wherever text is allowed (including another container such as a button).
allowWhere: '$text',
allowAttributes: Object.keys(this.attrs),
});
// Register `<drupal-icon>` as a block element in the DOM converter. This
// ensures that the DOM converter knows to handle the `<drupal-icon>` as a
// block element.
this.editor.editing.view.domConverter.blockElements.push('drupal-icon');
// @todo copy from Media, do we need it?
// this.editor.editing.view.domConverter.blockElements.push('drupal-icon');
}
/**
......
......@@ -26,9 +26,8 @@ ui_icons_ckeditor5_icon:
icon:
label: 'Icon'
elements:
- <drupal-icon>
- <drupal-icon data-icon-id>
- <drupal-icon data-icon-id data-icon-settings>
- <drupal-icon data-icon-id data-icon-settings class role aria-label aria-hidden>
conditions:
filter: icon_embed
toolbarItem: icon
......@@ -12,6 +12,7 @@ use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Template\Attribute;
use Drupal\filter\Attribute\Filter;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
......@@ -58,18 +59,6 @@ class IconEmbed extends FilterBase implements ContainerFactoryPluginInterface {
*/
protected $loggerFactory;
/**
* An array of counters for the recursive rendering protection.
*
* Each counter takes into account all the relevant information about the
* field and the referenced entity that is being rendered.
*
* @var array
*
* @see \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter::$recursiveRenderDepth
*/
protected static $recursiveRenderDepth = [];
/**
* Constructs a IconEmbed object.
*
......@@ -153,8 +142,6 @@ class IconEmbed extends FilterBase implements ContainerFactoryPluginInterface {
foreach ($xpath->query('//drupal-icon[normalize-space(@data-icon-id)!=""]') as $node) {
/** @var \DOMElement $node */
$icon_id = $node->getAttribute('data-icon-id');
// Delete the consumed attributes.
$node->removeAttribute('data-icon-id');
// Because of Ckeditor attributes system, we use a single attribute with
// serialized settings.
......@@ -168,19 +155,32 @@ class IconEmbed extends FilterBase implements ContainerFactoryPluginInterface {
$icon = $this->pluginManagerIconPack->getIcon($icon_id);
assert($icon === NULL || $icon instanceof IconDefinitionInterface);
// Use default settings if none set.
// @todo getRenderable should fallback to definition settings.
if (empty($settings)) {
[$icon_pack_id] = explode(':', $icon_id);
$settings = $this->pluginManagerIconPack->getExtractorFormDefaults($icon_pack_id);
}
if (!$icon) {
$this->loggerFactory->get('ui_icons')->error('During rendering of embedded icon: the icon item with ID "@id" does not exist.', ['@id' => $icon_id]);
}
$attributes = [];
if ($class = $node->getAttribute('class')) {
$attributes['class'] = explode(' ', $class);
}
if ($aria_label = $node->getAttribute('aria-label')) {
$attributes['aria-label'] = $aria_label;
}
if ($node->getAttribute('aria-hidden')) {
$attributes['aria-hidden'] = TRUE;
}
if ($role = $node->getAttribute('role')) {
$attributes['role'] = $role;
// The element with role="presentation" is not part of the accessibility
// tree and should not have an accessible name.
// @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/presentation_role
if (in_array($role, ['presentation', 'none'])) {
unset($attributes['aria-label']);
}
}
$build = $icon
? $this->getWrappedRenderable($icon, $settings)
? $this->getWrappedRenderable($icon, $settings, $attributes)
: $this->renderMissingIconIndicator($icon_id);
$this->renderIntoDomNode($build, $node, $result);
......@@ -215,15 +215,21 @@ class IconEmbed extends FilterBase implements ContainerFactoryPluginInterface {
* The icon to render.
* @param array $settings
* Settings to pass as context to the rendered icon.
* @param array $attributes
* Extra attributes to pass to the wrapper.
*
* @return array
* Renderable array.
*
* @todo wrapping, class and library as filter settings?
*/
protected function getWrappedRenderable(IconDefinitionInterface $icon, array $settings): array {
protected function getWrappedRenderable(IconDefinitionInterface $icon, array $settings, array $attributes): array {
$attributes['class'][] = 'drupal-icon';
$attributes = new Attribute($attributes);
$build = $icon->getRenderable($settings);
$build['#prefix'] = '<span class="drupal-icon">';
$build['#prefix'] = '<span' . $attributes . '>';
$build['#suffix'] = '</span>';
$build['#attached']['library'][] = 'ui_icons_text/icon.content';
......@@ -233,6 +239,8 @@ class IconEmbed extends FilterBase implements ContainerFactoryPluginInterface {
/**
* Renders the given render array into the given DOM node.
*
* @todo this is a copy from Media core, not sure we really need it.
*
* @param array $build
* The render array to render in isolation.
* @param \DOMNode $node
......@@ -306,13 +314,4 @@ class IconEmbed extends FilterBase implements ContainerFactoryPluginInterface {
$node->parentNode->removeChild($node);
}
/**
* {@inheritdoc}
*/
public function calculateDependencies(): array {
$dependencies = [];
// @todo ensure icon pack definition is still available?
return $dependencies;
}
}
<?php
/**
* @file
* Provides icon items.
*/
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_FORM_ID_alter().
*/
function ui_icons_text_form_filter_format_edit_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
// Add an additional validate callback so we can ensure the order of filters
// is correct.
$form['#validate'][] = 'ui_icons_text_filter_format_edit_form_validate';
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function ui_icons_text_form_filter_format_add_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
// Add an additional validate callback so we can ensure the order of filters
// is correct.
$form['#validate'][] = 'ui_icons_text_filter_format_edit_form_validate';
}
/**
* Validate callback to ensure filter order and allowed_html are compatible.
*
* This is a copy from media.module.
*/
function ui_icons_text_filter_format_edit_form_validate($form, FormStateInterface $form_state) {
if ($form_state->getTriggeringElement()['#name'] !== 'op') {
return;
}
$allowed_html_path = [
'filters',
'filter_html',
'settings',
'allowed_html',
];
$filter_html_settings_path = [
'filters',
'filter_html',
'settings',
];
$filter_html_enabled = $form_state->getValue([
'filters',
'filter_html',
'status',
]);
$icon_embed_enabled = $form_state->getValue([
'filters',
'icon_embed',
'status',
]);
if (!$icon_embed_enabled) {
return;
}
$get_filter_label = function ($filter_plugin_id) use ($form) {
return (string) $form['filters']['order'][$filter_plugin_id]['filter']['#markup'];
};
if (!$filter_html_enabled || !$form_state->getValue($allowed_html_path)) {
return;
}
/** @var \Drupal\filter\Entity\FilterFormat $filter_format */
$filter_format = $form_state->getFormObject()->getEntity();
$filter_html = clone $filter_format->filters()->get('filter_html');
$filter_html->setConfiguration(['settings' => $form_state->getValue($filter_html_settings_path)]);
$restrictions = $filter_html->getHTMLRestrictions();
$allowed = $restrictions['allowed'];
// Require `<drupal-icon>` HTML tag if filter_html is enabled.
if (!isset($allowed['drupal-icon'])) {
$form_state->setError($form['filters']['settings']['filter_html']['allowed_html'], t('The %icon-embed-filter-label filter requires <code>&lt;drupal-icon data-icon-id data-icon-settings class aria-label aria-hidden role&gt;</code> among the allowed HTML tags.', [
'%icon-embed-filter-label' => $get_filter_label('icon_embed'),
]));
}
else {
$required_attributes = [
'data-icon-id',
'data-icon-settings',
'class',
'aria-label',
'aria-hidden',
'role',
];
// If there are no attributes, the allowed item is set to FALSE,
// otherwise, it is set to an array.
if ($allowed['drupal-icon'] === FALSE) {
$missing_attributes = $required_attributes;
}
else {
$missing_attributes = array_diff($required_attributes, array_keys($allowed['drupal-icon']));
}
if ($missing_attributes) {
$form_state->setError($form['filters']['settings']['filter_html']['allowed_html'], t('The <code>&lt;drupal-icon&gt;</code> tag in the allowed HTML tags is missing the following attributes: <code>%list</code>.', [
'%list' => implode(', ', $missing_attributes),
]));
}
$filters = $form_state->getValue('filters');
// The icon filter must be after "filter_html", "filter_autop" and is
// canceled by "filter_html_escape".
$precedents = [
'filter_html',
'filter_autop',
];
$error_filters = [];
foreach ($precedents as $filter_name) {
// A filter that should run before icon embed filter.
$precedent = $filters[$filter_name];
if (empty($precedent['status']) || !isset($precedent['weight'])) {
continue;
}
if ($precedent['weight'] >= $filters['icon_embed']['weight']) {
$error_filters[$filter_name] = $get_filter_label($filter_name);
}
}
if (!empty($error_filters)) {
$error_message = \Drupal::translation()->formatPlural(
count($error_filters),
'The %icon-embed-filter-label filter needs to be placed after the %filter filter.',
'The %icon-embed-filter-label filter needs to be placed after the following filters: %filters.',
[
'%icon-embed-filter-label' => $get_filter_label('icon_embed'),
'%filter' => reset($error_filters),
'%filters' => implode(', ', $error_filters),
]
);
$form_state->setErrorByName('filters', $error_message);
}
if (isset($filters['filter_html_escape']['status']) && $filters['filter_html_escape']['status']) {
$error = t('The Embed icon will not work and should be removed if the %filter is enabled', ['%filter' => $get_filter_label('filter_html_escape')]);
$form_state->setErrorByName('filters', $error);
}
}
}
......@@ -45,6 +45,11 @@ parameters:
count: 2
path: modules/ui_icons_text/src/Plugin/Filter/IconEmbed.php
-
message: "#^Call to an undefined method Drupal\\\\Core\\\\Form\\\\FormInterface\\:\\:getEntity\\(\\)\\.$#"
count: 1
path: modules/ui_icons_text/ui_icons_text.module
-
message: "#^Parameter \\#1 \\$selector of class Drupal\\\\Core\\\\Ajax\\\\ReplaceCommand constructor expects string, null given\\.$#"
count: 1
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment