From 4baad7650fa4c44dbf4c2c0fcc6c18bf717775c6 Mon Sep 17 00:00:00 2001 From: Benjamin Kott <benjamin.kott@outlook.com> Date: Wed, 4 Jan 2023 15:22:48 +0100 Subject: [PATCH] [BUGFIX] Restore visibility for soft hyphens and non-breaking spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Non-breaking spaces and soft hyphens are now visible in the editor to help the editor to identify them visually. Keyboard shortcuts are now working for non-breaking spaces and soft hyphens and use more common defaults: - ctrl+shift+space for non-breaking space - ctrl+shift+dash for soft hyphen Please note that MacOS use different shortcuts: - alt+shift+space for non-breaking space - alt+shift+dash for soft hyphen The SoftHyphen plugin for CKEditor is now deprecated and replaced with a new Whitespace Plugin that handles non-breaking spaces and soft hyphens. Loading the SoftHyphen will trigger a console warning. Resolves: #99454 Releases: main Change-Id: I2a1c5edfd7e95f85c060746795231fda56b56f8c Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/77255 Tested-by: core-ci <typo3@b13.com> Tested-by: Benni Mack <benni@typo3.org> Reviewed-by: Benni Mack <benni@typo3.org> Reviewed-by: Frank Nägler <frank.naegler@typo3.com> Tested-by: Frank Nägler <frank.naegler@typo3.com> --- .../rte_ckeditor/plugin/soft-hyphen.ts | 27 +---- .../rte_ckeditor/plugin/whitespace.ts | 102 ++++++++++++++++++ .../Configuration/CKEditor5Migrator.php | 3 +- ...lityForSoftHyphensAndNonBreakingSpaces.rst | 64 +++++++++++ .../Configuration/RTE/Default.yaml | 2 +- .../Configuration/RTE/Editor/Plugins.yaml | 4 +- .../rte_ckeditor/Configuration/RTE/Full.yaml | 6 +- .../Resources/Public/Css/editor.css | 22 ++++ .../Public/JavaScript/plugin/soft-hyphen.js | 2 +- .../Public/JavaScript/plugin/whitespace.js | 13 +++ 10 files changed, 214 insertions(+), 31 deletions(-) create mode 100644 Build/Sources/TypeScript/rte_ckeditor/plugin/whitespace.ts create mode 100644 typo3/sysext/core/Documentation/Changelog/12.2/Deprecation-99454-RestoreVisibilityForSoftHyphensAndNonBreakingSpaces.rst create mode 100644 typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/plugin/whitespace.js diff --git a/Build/Sources/TypeScript/rte_ckeditor/plugin/soft-hyphen.ts b/Build/Sources/TypeScript/rte_ckeditor/plugin/soft-hyphen.ts index 4a10126e6b1a..9b8a09ced468 100644 --- a/Build/Sources/TypeScript/rte_ckeditor/plugin/soft-hyphen.ts +++ b/Build/Sources/TypeScript/rte_ckeditor/plugin/soft-hyphen.ts @@ -1,30 +1,11 @@ -import {UI, Core} from '@typo3/ckeditor5-bundle'; -import type {EditorWithUI} from '@ckeditor/ckeditor5-core/src/editor/editorwithui'; +import { Core } from '@typo3/ckeditor5-bundle'; +import Whitespace from '@typo3/rte-ckeditor/plugin/whitespace'; export default class SoftHyphen extends Core.Plugin { static readonly pluginName = 'SoftHyphen'; + static readonly requires = [Whitespace]; public init(): void { - const editor = this.editor as EditorWithUI; - - editor.ui.componentFactory.add(SoftHyphen.pluginName, locale => { - const button = new UI.ButtonView(locale); - button.label = 'Soft-Hyphen'; - // @todo introduce SVG loader - button.icon = '<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16"><defs><style>.cls-1{fill:none;}.cls-2{fill:#464646;}.cls-3{fill:url(#Unbenannter_Verlauf_19);}</style><linearGradient id="Unbenannter_Verlauf_19" x1="3" y1="8" x2="13" y2="8" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff" stop-opacity="0"/><stop offset="0.1" stop-color="#464646"/><stop offset="0.9" stop-color="#464646"/><stop offset="1" stop-opacity="0"/></linearGradient></defs><title>typo3_softhyphen</title><rect class="cls-1" width="16" height="16"/><path class="cls-2" d="M1,8a8.51,8.51,0,0,1,.4-2.7A6.12,6.12,0,0,1,2.79,3H4a9.55,9.55,0,0,0-.78,1.13A7,7,0,0,0,2.67,5.3a5.91,5.91,0,0,0-.32,1.27,9.35,9.35,0,0,0,0,2.86,5.91,5.91,0,0,0,.32,1.27,7,7,0,0,0,.55,1.17A9.55,9.55,0,0,0,4,13H2.79A6.12,6.12,0,0,1,1.4,10.7,8.51,8.51,0,0,1,1,8Z"/><path class="cls-2" d="M15,8a8.51,8.51,0,0,1-.4,2.7A6.12,6.12,0,0,1,13.21,13H12a9.55,9.55,0,0,0,.78-1.13,7,7,0,0,0,.55-1.17,5.91,5.91,0,0,0,.32-1.27,9.35,9.35,0,0,0,0-2.86,5.91,5.91,0,0,0-.32-1.27,7,7,0,0,0-.55-1.17A9.55,9.55,0,0,0,12,3h1.21A6.12,6.12,0,0,1,14.6,5.3,8.51,8.51,0,0,1,15,8Z"/><rect class="cls-3" x="3" y="7" width="10" height="2"/></svg>'; - button.keystroke = 'Ctrl!+-'; - button.on( 'execute', () => this.addSoftHyphen()); - return button; - }); - } - - private addSoftHyphen(): void { - const editor = this.editor as EditorWithUI; - - editor.model.change( writer => { - editor.model.insertContent( - writer.createText('\u00AD') - ); - }); + console.warn('The TYPO3 CKEditor5 SoftHyphen plugin is deprecated and will be removed with v13. Please use the Whitespace plugin instead.'); } } diff --git a/Build/Sources/TypeScript/rte_ckeditor/plugin/whitespace.ts b/Build/Sources/TypeScript/rte_ckeditor/plugin/whitespace.ts new file mode 100644 index 000000000000..91d8154a6f30 --- /dev/null +++ b/Build/Sources/TypeScript/rte_ckeditor/plugin/whitespace.ts @@ -0,0 +1,102 @@ +import { Core, UI, Utils } from '@typo3/ckeditor5-bundle'; +import type { EditorWithUI } from '@ckeditor/ckeditor5-core/src/editor/editorwithui'; + +/** + * CKEditor5 Whitespace Plugin + * + * Add support for non breaking spaces + * - Make non breaking spaces visible + * - Shortcut for adding non breaking space: + * - alt+shift+space on MacOS + * - ctrl+shift+space on all other Systems + * + * Add support for soft hyphen + * - Make soft hyphens visible + * - Register button for editor ui: softhyphen + * - Shortcut for adding non breaking space: + * - alt+shift+dash on MacOS + * - ctrl+shift+dash on all other Systems + */ +export default class Whitespace extends Core.Plugin { + static readonly pluginName = 'Whitespace'; + + public init(): void { + const editor = this.editor as EditorWithUI; + + editor.ui.componentFactory.add('softhyphen', locale => { + const button = new UI.ButtonView(locale); + button.label = 'Soft-Hyphen'; + button.icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" xml:space="preserve"><path d="M4.25 3C3.082 4.683 2 6.917 2 10.026 2 13.083 3.114 15.282 4.25 17H3c-1.008-1.425-2-3.624-2-6.974.016-3.384.992-5.583 2-7.026h1.25zM17 3c1.008 1.443 1.984 3.642 2 7.026 0 3.35-.992 5.549-2 6.974h-1.25c1.136-1.718 2.25-3.917 2.25-6.974 0-3.11-1.082-5.343-2.25-7.026H17zM6 9h8v2H6z"/></svg>'; + button.on('execute', () => this.insertSoftHyphen()); + return button; + }); + + // CKEditor should map Ctrl to Cmd on MacOs, but for + // some reason the shortcut is blocked and cannot be + // overwritten. + // + // For this reason we are using "Alt" as controlKey on MacOS. + // All other System use "Ctrl" as controlKey. + const controlKey = Utils.env.isMac ? 'Alt' : 'Ctrl'; + editor.keystrokes.set([controlKey, 'Shift', 189], (data, cancel) => { + this.insertSoftHyphen(); + cancel(); + }); + + editor.keystrokes.set([controlKey, 'Shift', 'Space'], (data, cancel) => { + this.insertNonBreakingSpace(); + cancel(); + }); + + editor.conversion.for('editingDowncast').add(downcastDispatcher => { + downcastDispatcher.on('insert:$text', (evt, data, conversionApi) => { + if (!conversionApi.consumable.consume(data.item, evt.name)) { + return; + } + + const viewWriter = conversionApi.writer; + const chunks = data.item.data + .split(/([\u00AD,\u00A0])/) + .filter((value: String) => value !== ''); + let currentPosition = data.range.start; + + chunks.forEach((chunk) => { + viewWriter.insert( + conversionApi.mapper.toViewPosition(currentPosition), + viewWriter.createText(chunk) + ); + + if (chunk === '\u00AD' || chunk === '\u00A0') { + const className = chunk === '\u00AD' ? 'softhyphen' : 'nbsp'; + const wrapper = viewWriter.createAttributeElement('span', { class: 'ck ck-' + className }); + const wrapperRange = viewWriter.createRange( + conversionApi.mapper.toViewPosition(currentPosition), + conversionApi.mapper.toViewPosition(currentPosition.getShiftedBy(chunk.length)) + ); + viewWriter.wrap(wrapperRange, wrapper); + } + + currentPosition = currentPosition.getShiftedBy(chunk.length); + }); + }, { priority: 'high' }); + }); + } + + private insertNonBreakingSpace(): void { + const editor = this.editor as EditorWithUI; + + editor.model.change(writer => { + const insertPosition = editor.model.document.selection.getFirstPosition(); + writer.insertText('\u00A0', insertPosition); + }); + } + + private insertSoftHyphen(): void { + const editor = this.editor as EditorWithUI; + + editor.model.change(writer => { + const insertPosition = editor.model.document.selection.getFirstPosition(); + writer.insertText('\u00AD', insertPosition); + }); + } +} diff --git a/typo3/sysext/core/Classes/Configuration/CKEditor5Migrator.php b/typo3/sysext/core/Classes/Configuration/CKEditor5Migrator.php index f0a809215b9d..35344adf0cce 100644 --- a/typo3/sysext/core/Classes/Configuration/CKEditor5Migrator.php +++ b/typo3/sysext/core/Classes/Configuration/CKEditor5Migrator.php @@ -23,7 +23,7 @@ namespace TYPO3\CMS\Core\Configuration; class CKEditor5Migrator { private const TOOLBAR_GROUPS_MAP = [ - 'basicstyles' => ['bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript'], + 'basicstyles' => ['bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript', 'softhyphen'], 'format' => ['heading'], 'styles' => ['style'], 'list' => ['numberedList', 'bulletedList'], @@ -58,6 +58,7 @@ class CKEditor5Migrator 'Format' => ['heading'], 'BasicStyle' => ['heading'], 'Table' => ['insertTable'], + 'SoftHyphen' => ['softhyphen'], 'specialcharacters' => ['specialCharacters'], 'specialchar' => ['specialCharacters'], ]; diff --git a/typo3/sysext/core/Documentation/Changelog/12.2/Deprecation-99454-RestoreVisibilityForSoftHyphensAndNonBreakingSpaces.rst b/typo3/sysext/core/Documentation/Changelog/12.2/Deprecation-99454-RestoreVisibilityForSoftHyphensAndNonBreakingSpaces.rst new file mode 100644 index 000000000000..c63e24c9a62a --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/12.2/Deprecation-99454-RestoreVisibilityForSoftHyphensAndNonBreakingSpaces.rst @@ -0,0 +1,64 @@ +.. include:: /Includes.rst.txt + +.. _deprecation-99454-1672842347: + +================================================================================= +Deprecation: #99454 - Restore visibility for soft hyphens and non-breaking spaces +================================================================================= + +See :issue:`99454` + +Description +=========== + +Non-breaking spaces and soft hyphens are now visible in +the editor to help the editor to identify them visually. + +Keyboard shortcuts are now working for non-breaking spaces +and soft hyphens and use more common defaults: +- ctrl+shift+space for non-breaking space +- ctrl+shift+dash for soft hyphen + +The SoftHyphen plugin for CKEditor is now deprecated and +replaced with a new Whitespace Plugin that handles +non-breaking spaces and soft hyphens. Loading the +SoftHyphen will trigger a console warning. + + +Impact +====== + +Including the SoftHyphen will trigger a deprecation warning. + + +Affected installations +====================== + +All installations that include the Plugin manually. + + +Migration +========= + +Replace the module to resolve the deprecation. + +Before +~~~~~~ + +.. code-block:: yaml + editor: + config: + importModules: + - '@typo3/rte-ckeditor/plugin/soft-hyphen.js' + +After +~~~~~ + +.. code-block:: yaml + editor: + config: + importModules: + - '@typo3/rte-ckeditor/plugin/whitespace.js' + + +.. index:: Backend, JavaScript, RTE, NotScanned, ext:rte_ckeditor diff --git a/typo3/sysext/rte_ckeditor/Configuration/RTE/Default.yaml b/typo3/sysext/rte_ckeditor/Configuration/RTE/Default.yaml index 7678c4814200..d45a1eaf63a7 100644 --- a/typo3/sysext/rte_ckeditor/Configuration/RTE/Default.yaml +++ b/typo3/sysext/rte_ckeditor/Configuration/RTE/Default.yaml @@ -18,6 +18,7 @@ editor: - italic - subscript - superscript + - softhyphen - '|' - bulletedList - numberedList @@ -26,7 +27,6 @@ editor: - '|' - findAndReplace - link - - SoftHyphen - '|' - removeFormat - undo diff --git a/typo3/sysext/rte_ckeditor/Configuration/RTE/Editor/Plugins.yaml b/typo3/sysext/rte_ckeditor/Configuration/RTE/Editor/Plugins.yaml index 3da71263fd5d..93a507d22967 100644 --- a/typo3/sysext/rte_ckeditor/Configuration/RTE/Editor/Plugins.yaml +++ b/typo3/sysext/rte_ckeditor/Configuration/RTE/Editor/Plugins.yaml @@ -7,8 +7,8 @@ editor: # load modules for plugins when CKEditor is initialized # see CKEditor plugin API for details importModules: - # softhyphen plugin for adding ctrl+dash support to insert a conditional word break - - '@typo3/rte-ckeditor/plugin/soft-hyphen.js' + # Plugin for whitespace control like soft hypens and non breaking spaces + - '@typo3/rte-ckeditor/plugin/whitespace.js' - '@typo3/rte-ckeditor/plugin/typo3-link.js' # Configure global wordCount plugin defaults diff --git a/typo3/sysext/rte_ckeditor/Configuration/RTE/Full.yaml b/typo3/sysext/rte_ckeditor/Configuration/RTE/Full.yaml index 18cc80f2637f..f69918a6beda 100644 --- a/typo3/sysext/rte_ckeditor/Configuration/RTE/Full.yaml +++ b/typo3/sysext/rte_ckeditor/Configuration/RTE/Full.yaml @@ -32,6 +32,7 @@ editor: toolbar: items: - clipboard + - removeFormat - undo - redo # grouping separator @@ -40,7 +41,6 @@ editor: - selectAll - '|' - link - - SoftHyphen - insertTable - tableColumn - tableRow @@ -56,14 +56,14 @@ editor: - strikethrough - subscript - superscript - - alignment - - removeFormat + - softhyphen - '|' - bulletedList - numberedList - blockQuote - indent - outdent + - alignment - '|' - specialCharacters - '-' diff --git a/typo3/sysext/rte_ckeditor/Resources/Public/Css/editor.css b/typo3/sysext/rte_ckeditor/Resources/Public/Css/editor.css index 96571bf96a66..c7453fd57de6 100644 --- a/typo3/sysext/rte_ckeditor/Resources/Public/Css/editor.css +++ b/typo3/sysext/rte_ckeditor/Resources/Public/Css/editor.css @@ -12,3 +12,25 @@ .ck.ck-word-count { margin-top: .5rem; } + +.ck.ck-softhyphen { + border-radius: .15em; + color: #0078e6; + background-color: rgba(0,120,230,.1); +} + +.ck.ck-softhyphen:before { + content: '–'; + margin-left: .1em; + margin-right: .1em; +} + +.ck.ck-nbsp { + border-radius: .15em; + color: #c83c3c; + background-color: rgba(200,60,60,.1); +} + +.ck.ck-nbsp:before { + content: ' '; +} diff --git a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/plugin/soft-hyphen.js b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/plugin/soft-hyphen.js index 42715fc9e8f1..d1909b46737a 100644 --- a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/plugin/soft-hyphen.js +++ b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/plugin/soft-hyphen.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{UI,Core}from"@typo3/ckeditor5-bundle.js";export default class SoftHyphen extends Core.Plugin{init(){this.editor.ui.componentFactory.add(SoftHyphen.pluginName,(t=>{const e=new UI.ButtonView(t);return e.label="Soft-Hyphen",e.icon='<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16"><defs><style>.cls-1{fill:none;}.cls-2{fill:#464646;}.cls-3{fill:url(#Unbenannter_Verlauf_19);}</style><linearGradient id="Unbenannter_Verlauf_19" x1="3" y1="8" x2="13" y2="8" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff" stop-opacity="0"/><stop offset="0.1" stop-color="#464646"/><stop offset="0.9" stop-color="#464646"/><stop offset="1" stop-opacity="0"/></linearGradient></defs><title>typo3_softhyphen</title><rect class="cls-1" width="16" height="16"/><path class="cls-2" d="M1,8a8.51,8.51,0,0,1,.4-2.7A6.12,6.12,0,0,1,2.79,3H4a9.55,9.55,0,0,0-.78,1.13A7,7,0,0,0,2.67,5.3a5.91,5.91,0,0,0-.32,1.27,9.35,9.35,0,0,0,0,2.86,5.91,5.91,0,0,0,.32,1.27,7,7,0,0,0,.55,1.17A9.55,9.55,0,0,0,4,13H2.79A6.12,6.12,0,0,1,1.4,10.7,8.51,8.51,0,0,1,1,8Z"/><path class="cls-2" d="M15,8a8.51,8.51,0,0,1-.4,2.7A6.12,6.12,0,0,1,13.21,13H12a9.55,9.55,0,0,0,.78-1.13,7,7,0,0,0,.55-1.17,5.91,5.91,0,0,0,.32-1.27,9.35,9.35,0,0,0,0-2.86,5.91,5.91,0,0,0-.32-1.27,7,7,0,0,0-.55-1.17A9.55,9.55,0,0,0,12,3h1.21A6.12,6.12,0,0,1,14.6,5.3,8.51,8.51,0,0,1,15,8Z"/><rect class="cls-3" x="3" y="7" width="10" height="2"/></svg>',e.keystroke="Ctrl!+-",e.on("execute",(()=>this.addSoftHyphen())),e}))}addSoftHyphen(){const t=this.editor;t.model.change((e=>{t.model.insertContent(e.createText("Â"))}))}}SoftHyphen.pluginName="SoftHyphen"; \ No newline at end of file +import{Core}from"@typo3/ckeditor5-bundle.js";import Whitespace from"@typo3/rte-ckeditor/plugin/whitespace.js";export default class SoftHyphen extends Core.Plugin{init(){console.warn("The TYPO3 CKEditor5 SoftHyphen plugin is deprecated and will be removed with v13. Please use the Whitespace plugin instead.")}}SoftHyphen.pluginName="SoftHyphen",SoftHyphen.requires=[Whitespace]; \ No newline at end of file diff --git a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/plugin/whitespace.js b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/plugin/whitespace.js new file mode 100644 index 000000000000..17ca53db262e --- /dev/null +++ b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/plugin/whitespace.js @@ -0,0 +1,13 @@ +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +import{Core,UI,Utils}from"@typo3/ckeditor5-bundle.js";export default class Whitespace extends Core.Plugin{init(){const e=this.editor;e.ui.componentFactory.add("softhyphen",(e=>{const t=new UI.ButtonView(e);return t.label="Soft-Hyphen",t.icon='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" xml:space="preserve"><path d="M4.25 3C3.082 4.683 2 6.917 2 10.026 2 13.083 3.114 15.282 4.25 17H3c-1.008-1.425-2-3.624-2-6.974.016-3.384.992-5.583 2-7.026h1.25zM17 3c1.008 1.443 1.984 3.642 2 7.026 0 3.35-.992 5.549-2 6.974h-1.25c1.136-1.718 2.25-3.917 2.25-6.974 0-3.11-1.082-5.343-2.25-7.026H17zM6 9h8v2H6z"/></svg>',t.on("execute",(()=>this.insertSoftHyphen())),t}));const t=Utils.env.isMac?"Alt":"Ctrl";e.keystrokes.set([t,"Shift",189],((e,t)=>{this.insertSoftHyphen(),t()})),e.keystrokes.set([t,"Shift","Space"],((e,t)=>{this.insertNonBreakingSpace(),t()})),e.conversion.for("editingDowncast").add((e=>{e.on("insert:$text",((e,t,i)=>{if(!i.consumable.consume(t.item,e.name))return;const n=i.writer,o=t.item.data.split(/([\u00AD,\u00A0])/).filter((e=>""!==e));let s=t.range.start;o.forEach((e=>{if(n.insert(i.mapper.toViewPosition(s),n.createText(e)),"Â"===e||" "===e){const t="Â"===e?"softhyphen":"nbsp",o=n.createAttributeElement("span",{class:"ck ck-"+t}),r=n.createRange(i.mapper.toViewPosition(s),i.mapper.toViewPosition(s.getShiftedBy(e.length)));n.wrap(r,o)}s=s.getShiftedBy(e.length)}))}),{priority:"high"})}))}insertNonBreakingSpace(){const e=this.editor;e.model.change((t=>{const i=e.model.document.selection.getFirstPosition();t.insertText(" ",i)}))}insertSoftHyphen(){const e=this.editor;e.model.change((t=>{const i=e.model.document.selection.getFirstPosition();t.insertText("Â",i)}))}}Whitespace.pluginName="Whitespace"; \ No newline at end of file -- GitLab