-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
adjacentlistssupport.ts
110 lines (89 loc) · 3.1 KB
/
adjacentlistssupport.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module list/list/adjacentlistssupport
*/
import type { GetCallback } from 'ckeditor5/src/utils.js';
import { Plugin } from 'ckeditor5/src/core.js';
import type { UpcastElementEvent, ViewElement } from 'ckeditor5/src/engine.js';
export default class AdjacentListsSupport extends Plugin {
/**
* @inheritDoc
*/
public static get pluginName() {
return 'AdjacentListsSupport' as const;
}
/**
* @inheritDoc
*/
public init(): void {
const editor = this.editor;
const model = editor.model;
model.schema.register( 'listSeparator', {
allowWhere: '$block',
isBlock: true
} );
editor.conversion.for( 'upcast' )
// Add a list separator element between similar list elements on upcast.
.add( dispatcher => {
dispatcher.on<UpcastElementEvent>( 'element:ol', listSeparatorUpcastConverter() );
dispatcher.on<UpcastElementEvent>( 'element:ul', listSeparatorUpcastConverter() );
} )
// View-to-model transformation.
.elementToElement( {
model: 'listSeparator',
view: 'ck-list-separator'
} );
// The list separator element should exist in the view, but should be invisible (hidden).
editor.conversion.for( 'editingDowncast' ).elementToElement( {
model: 'listSeparator',
view: {
name: 'div',
classes: [ 'ck-list-separator', 'ck-hidden' ]
}
} );
// The list separator element should not exist in the output data.
editor.conversion.for( 'dataDowncast' ).elementToElement( {
model: 'listSeparator',
view: ( modelElement, conversionApi ) => {
const viewElement = conversionApi.writer.createContainerElement( 'ck-list-separator' );
conversionApi.writer.setCustomProperty( 'dataPipeline:transparentRendering', true, viewElement );
viewElement.getFillerOffset = () => null;
return viewElement;
}
} );
}
}
/**
* Inserts a list separator element between two lists of the same type (`ol` + `ol` or `ul` + `ul`).
*/
function listSeparatorUpcastConverter(): GetCallback<UpcastElementEvent> {
return ( evt, data, conversionApi ) => {
const element: ViewElement = data.viewItem;
const nextSibling = element.nextSibling as ViewElement | null;
if ( !nextSibling ) {
return;
}
if ( element.name !== nextSibling.name ) {
return;
}
if ( !data.modelRange ) {
Object.assign( data, conversionApi.convertChildren( data.viewItem, data.modelCursor ) );
}
const writer = conversionApi.writer;
const modelElement = writer.createElement( 'listSeparator' );
// Try to insert a list separator element on the current model cursor position.
if ( !conversionApi.safeInsert( modelElement, data.modelCursor ) ) {
return;
}
const parts = conversionApi.getSplitParts( modelElement );
// Extend the model range with the range of the created list separator element.
data.modelRange = writer.createRange(
data.modelRange!.start,
writer.createPositionAfter( parts[ parts.length - 1 ] )
);
conversionApi.updateConversionResult( modelElement, data );
};
}