8
0
Просмотр исходного кода

List styles will be inherited correctly when pasting a list into another list.

Kamil Piechaczek 5 лет назад
Родитель
Сommit
9bdbd32a21

+ 1 - 1
packages/ckeditor5-list/package.json

@@ -10,6 +10,7 @@
     "ckeditor5-plugin"
   ],
   "dependencies": {
+    "@ckeditor/ckeditor5-clipboard": "^23.0.0",
     "@ckeditor/ckeditor5-core": "^23.0.0",
     "@ckeditor/ckeditor5-engine": "^23.0.0",
     "@ckeditor/ckeditor5-paragraph": "^23.0.0",
@@ -19,7 +20,6 @@
   "devDependencies": {
     "@ckeditor/ckeditor5-basic-styles": "^23.0.0",
     "@ckeditor/ckeditor5-block-quote": "^23.0.0",
-    "@ckeditor/ckeditor5-clipboard": "^23.0.0",
     "@ckeditor/ckeditor5-essentials": "^23.0.0",
     "@ckeditor/ckeditor5-editor-classic": "^23.0.0",
     "@ckeditor/ckeditor5-enter": "^23.0.0",

+ 48 - 1
packages/ckeditor5-list/src/liststyleediting.js

@@ -8,6 +8,7 @@
  */
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
 import ListEditing from './listediting';
 import ListStyleCommand from './liststylecommand';
 import { getSiblingListItem, getSiblingNodes } from './utils';
@@ -29,7 +30,7 @@ export default class ListStyleEditing extends Plugin {
 	 * @inheritDoc
 	 */
 	static get requires() {
-		return [ ListEditing ];
+		return [ ListEditing, Clipboard ];
 	}
 
 	/**
@@ -437,6 +438,13 @@ function fixListAfterOutdentListCommand( editor ) {
 // @param {module:core/editor/editor~Editor} editor
 // @returns {Function}
 function fixListStyleAttributeOnListItemElements( editor ) {
+	// A flag that determines whether the inserted content comes from the clipboard.
+	let contentFromClipboard;
+
+	editor.listenTo( editor.plugins.get( Clipboard ), 'inputTransformation', () => {
+		contentFromClipboard = true;
+	} );
+
 	return writer => {
 		let wasFixed = false;
 
@@ -450,6 +458,45 @@ function fixListStyleAttributeOnListItemElements( editor ) {
 			return wasFixed;
 		}
 
+		// Adjust the `listStyle` attribute for inserted (pasted) items. See #8160.
+		//
+		// ■ List item 1. // [listStyle="square", listType="bulleted"]
+		//     ○ List item 1.1. // [listStyle="circle", listType="bulleted"]
+		//     ○ [] (selection is here)
+		//
+		// Then, pasting a list with different attributes (listStyle, listType):
+		//
+		// 1. First. // [listStyle="decimal", listType="numbered"]
+		// 2. Second // [listStyle="decimal", listType="numbered"]
+		//
+		// The `listType` attribute will be corrected by the `ListEditing` converters.
+		// We need to adjust the `listStyle` attribute. Expected structure:
+		//
+		// ■ List item 1. // [listStyle="square", listType="bulleted"]
+		//     ○ List item 1.1. // [listStyle="circle", listType="bulleted"]
+		//     ○ First. // [listStyle="circle", listType="bulleted"]
+		//     ○ Second // [listStyle="circle", listType="bulleted"]
+		const firstPreviousSibling = insertedListItems[ 0 ].previousSibling;
+
+		if ( firstPreviousSibling && contentFromClipboard ) {
+			contentFromClipboard = false;
+
+			const listIndent = firstPreviousSibling.getAttribute( 'listIndent' );
+			const listStyle = firstPreviousSibling.getAttribute( 'listStyle' );
+
+			const itemsToUpdate = insertedListItems.filter( i => {
+				return i.getAttribute( 'listIndent' ) === listIndent && i.getAttribute( 'listStyle' ) !== listStyle;
+			} );
+
+			if ( itemsToUpdate.length ) {
+				for ( const item of itemsToUpdate ) {
+					writer.setAttribute( 'listStyle', listStyle, item );
+				}
+
+				return true;
+			}
+		}
+
 		// Check whether the last inserted element is next to the `listItem` element.
 		//
 		// ■ Paragraph[]  // <-- The inserted item.

+ 149 - 4
packages/ckeditor5-list/tests/liststyleediting.js

@@ -3,6 +3,8 @@
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
+/* global document */
+
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import Typing from '@ckeditor/ckeditor5-typing/src/typing';
@@ -13,13 +15,17 @@ import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils
 import ListStyleEditing from '../src/liststyleediting';
 import TodoListEditing from '../src/todolistediting';
 import ListStyleCommand from '../src/liststylecommand';
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
 
 describe( 'ListStyleEditing', () => {
-	let editor, model, view;
+	let editor, model, view, element;
 
 	beforeEach( () => {
-		return VirtualTestEditor
-			.create( {
+		element = document.createElement( 'div' );
+		document.body.append( element );
+
+		return ClassicTestEditor
+			.create( element, {
 				plugins: [ Paragraph, ListStyleEditing, UndoEditing ]
 			} )
 			.then( newEditor => {
@@ -30,7 +36,10 @@ describe( 'ListStyleEditing', () => {
 	} );
 
 	afterEach( () => {
-		return editor.destroy();
+		return editor.destroy()
+			.then( () => {
+				element.remove();
+			} );
 	} );
 
 	it( 'should have pluginName', () => {
@@ -1459,5 +1468,141 @@ describe( 'ListStyleEditing', () => {
 				} );
 			}
 		} );
+
+		// #8160
+		describe( 'pasting a into another list', () => {
+			it( 'should inherit attributes from the previous sibling element (collapsed selection)', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo Bar</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+
+				pasteHtml( editor,
+					'<ul style="list-style-type: square">' +
+						'<li>Foo 1</li>' +
+						'<li>Foo 2</li>' +
+					'</ul>'
+				);
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo Bar</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo 1</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo 2[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+			} );
+
+			it( 'should inherit attributes from the previous sibling element (non-collapsed selection)', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo Bar</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">[Foo]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+
+				pasteHtml( editor,
+					'<ul style="list-style-type: square">' +
+						'<li>Foo 1</li>' +
+						'<li>Foo 2</li>' +
+					'</ul>'
+				);
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo Bar</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo 1</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo 2[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+			} );
+
+			it( 'should inherit attributes from the previous sibling element (non-collapsed selection over a few elements)', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo Bar</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">[Foo 1.</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo 2.</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo 3.]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+
+				pasteHtml( editor,
+					'<ul style="list-style-type: square">' +
+						'<li>Foo 1</li>' +
+						'<li>Foo 2</li>' +
+					'</ul>'
+				);
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo Bar</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo 1</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo 2[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+			} );
+
+			it( 'should do nothing when pasting the similar list', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo Bar</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+
+				pasteHtml( editor,
+					'<ol style="list-style-type: decimal">' +
+						'<li>Foo</li>' +
+					'</ol>'
+				);
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo Bar</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">Foo[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+			} );
+
+			it( 'should replace the entire list if selected', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">[Foo Bar]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+
+				pasteHtml( editor,
+					'<ul style="list-style-type: square">' +
+						'<li>Foo</li>' +
+					'</ul>'
+				);
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="1" listStyle="square" listType="bulleted">Foo[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+			} );
+
+			function pasteHtml( editor, html ) {
+				editor.editing.view.document.fire( 'paste', {
+					dataTransfer: createDataTransfer( { 'text/html': html } ),
+					stopPropagation() {},
+					preventDefault() {}
+				} );
+			}
+
+			function createDataTransfer( data ) {
+				return {
+					getData( type ) {
+						return data[ type ];
+					},
+					setData() {}
+				};
+			}
+		} );
 	} );
 } );