Skip to content

Commit c290d01

Browse files
committed
WYSIWYG: Improved a range of text direction/alignment scenarios
- Removes 'span' from being a valid part of alignment formats so it's not used to align contents, since it's going to mostly be an inline format, wheras you'd really want alignment on the parent block. - Adds direction cleaning to all direction change events, to remove direction styles and child direction controls which may complicate matters and cause direction changes not to show. - Makes text direction controls work with table cell range selections, which TinyMCE does not consider by default, via manual handling. For #4843
1 parent 16327cf commit c290d01

File tree

2 files changed

+71
-11
lines changed

2 files changed

+71
-11
lines changed

resources/js/wysiwyg/config.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import {getPlugin as getAboutPlugin} from './plugins-about';
1414
import {getPlugin as getDetailsPlugin} from './plugins-details';
1515
import {getPlugin as getTableAdditionsPlugin} from './plugins-table-additions';
1616
import {getPlugin as getTasklistPlugin} from './plugins-tasklist';
17-
import {handleClearFormattingOnTableCells, handleEmbedAlignmentChanges} from './fixes';
17+
import {
18+
handleTableCellRangeEvents,
19+
handleEmbedAlignmentChanges,
20+
handleTextDirectionCleaning,
21+
} from './fixes';
1822

1923
const styleFormats = [
2024
{title: 'Large Header', format: 'h2', preview: 'color: blue;'},
@@ -37,9 +41,9 @@ const styleFormats = [
3741
];
3842

3943
const formats = {
40-
alignleft: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img,iframe,video,span', classes: 'align-left'},
41-
aligncenter: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img,iframe,video,span', classes: 'align-center'},
42-
alignright: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img,iframe,video,span', classes: 'align-right'},
44+
alignleft: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img,iframe,video', classes: 'align-left'},
45+
aligncenter: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img,iframe,video', classes: 'align-center'},
46+
alignright: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img,iframe,video', classes: 'align-right'},
4347
calloutsuccess: {block: 'p', exact: true, attributes: {class: 'callout success'}},
4448
calloutinfo: {block: 'p', exact: true, attributes: {class: 'callout info'}},
4549
calloutwarning: {block: 'p', exact: true, attributes: {class: 'callout warning'}},
@@ -194,7 +198,8 @@ function getSetupCallback(options) {
194198
});
195199

196200
handleEmbedAlignmentChanges(editor);
197-
handleClearFormattingOnTableCells(editor);
201+
handleTableCellRangeEvents(editor);
202+
handleTextDirectionCleaning(editor);
198203

199204
// Custom handler hook
200205
window.$events.emitPublic(options.containerElement, 'editor-tinymce::setup', {editor});

resources/js/wysiwyg/fixes.js

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,30 @@ export function handleEmbedAlignmentChanges(editor) {
5555
}
5656

5757
/**
58-
* TinyMCE does not seem to do a great job on clearing styles in complex
59-
* scenarios (like copied word content) when a range of table cells
60-
* are selected. This tracks the selected table cells, and watches
61-
* for clear formatting events, so some manual cleanup can be performed.
62-
*
58+
* Cleans up the direction property for an element.
59+
* Removes all inline direction control from child elements.
60+
* Removes non "dir" attribute direction control from provided element.
61+
* @param {HTMLElement} element
62+
*/
63+
function cleanElementDirection(element) {
64+
const directionChildren = element.querySelectorAll('[dir],[style*="direction"],[style*="text-align"]');
65+
for (const child of directionChildren) {
66+
child.removeAttribute('dir');
67+
child.style.direction = null;
68+
child.style.textAlign = null;
69+
}
70+
element.style.direction = null;
71+
element.style.textAlign = null;
72+
}
73+
74+
/**
75+
* This tracks table cell range selection, so we can apply custom handling where
76+
* required to actions applied to such selections.
6377
* The events used don't seem to be advertised by TinyMCE.
6478
* Found at https://github.com/tinymce/tinymce/blob/6.8.3/modules/tinymce/src/models/dom/main/ts/table/api/Events.ts
6579
* @param {Editor} editor
6680
*/
67-
export function handleClearFormattingOnTableCells(editor) {
81+
export function handleTableCellRangeEvents(editor) {
6882
/** @var {HTMLTableCellElement[]} * */
6983
let selectedCells = [];
7084

@@ -75,6 +89,10 @@ export function handleClearFormattingOnTableCells(editor) {
7589
selectedCells = [];
7690
});
7791

92+
// TinyMCE does not seem to do a great job on clearing styles in complex
93+
// scenarios (like copied word content) when a range of table cells
94+
// are selected. Here we watch for clear formatting events, so some manual
95+
// cleanup can be performed.
7896
const attrsToRemove = ['class', 'style', 'width', 'height'];
7997
editor.on('FormatRemove', () => {
8098
for (const cell of selectedCells) {
@@ -83,4 +101,41 @@ export function handleClearFormattingOnTableCells(editor) {
83101
}
84102
}
85103
});
104+
105+
// TinyMCE does not apply direction events to table cell range selections
106+
// so here we hastily patch in that ability by setting the direction ourselves
107+
// when a direction event is fired.
108+
editor.on('ExecCommand', event => {
109+
const command = event.command;
110+
if (command !== 'mceDirectionLTR' && command !== 'mceDirectionRTL') {
111+
return;
112+
}
113+
114+
const dir = command === 'mceDirectionLTR' ? 'ltr' : 'rtl';
115+
for (const cell of selectedCells) {
116+
cell.setAttribute('dir', dir);
117+
cleanElementDirection(cell);
118+
}
119+
});
120+
}
121+
122+
/**
123+
* Direction control might not work if there are other unexpected direction-handling styles
124+
* or attributes involved nearby. This watches for direction change events to clean
125+
* up direction controls, removing non-dir-attr direction controls, while removing
126+
* directions from child elements that may be involved.
127+
* @param {Editor} editor
128+
*/
129+
export function handleTextDirectionCleaning(editor) {
130+
editor.on('ExecCommand', event => {
131+
const command = event.command;
132+
if (command !== 'mceDirectionLTR' && command !== 'mceDirectionRTL') {
133+
return;
134+
}
135+
136+
const blocks = editor.selection.getSelectedBlocks();
137+
for (const block of blocks) {
138+
cleanElementDirection(block);
139+
}
140+
});
86141
}

0 commit comments

Comments
 (0)