UNPKG

7.21 kBTypeScriptView Raw
1/**
2 * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4 */
5/**
6 * @module list/list/listediting
7 */
8import { Plugin, type Editor } from 'ckeditor5/src/core.js';
9import type { DowncastWriter, Element, ViewElement, ViewAttributeElement, Writer } from 'ckeditor5/src/engine.js';
10import { Delete } from 'ckeditor5/src/typing.js';
11import { Enter } from 'ckeditor5/src/enter.js';
12import ListUtils from './listutils.js';
13import { ListBlocksIterable } from './utils/listwalker.js';
14import { ClipboardPipeline } from 'ckeditor5/src/clipboard.js';
15import '../../theme/documentlist.css';
16import '../../theme/list.css';
17export type ListType = 'numbered' | 'bulleted' | 'todo' | 'customNumbered' | 'customBulleted';
18/**
19 * Map of model attributes applicable to list blocks.
20 */
21export interface ListItemAttributesMap {
22 listType?: ListType;
23 listIndent?: number;
24 listItemId?: string;
25}
26/**
27 * The editing part of the document-list feature. It handles creating, editing and removing lists and list items.
28 */
29export default class ListEditing extends Plugin {
30 /**
31 * The list of registered downcast strategies.
32 */
33 private readonly _downcastStrategies;
34 /**
35 * @inheritDoc
36 */
37 static get pluginName(): "ListEditing";
38 /**
39 * @inheritDoc
40 */
41 static get requires(): readonly [typeof Enter, typeof Delete, typeof ListUtils, typeof ClipboardPipeline];
42 /**
43 * @inheritDoc
44 */
45 constructor(editor: Editor);
46 /**
47 * @inheritDoc
48 */
49 init(): void;
50 /**
51 * @inheritDoc
52 */
53 afterInit(): void;
54 /**
55 * Registers a downcast strategy.
56 *
57 * **Note**: Strategies must be registered in the `Plugin#init()` phase so that it can be applied
58 * in the `ListEditing#afterInit()`.
59 *
60 * @param strategy The downcast strategy to register.
61 */
62 registerDowncastStrategy(strategy: DowncastStrategy): void;
63 /**
64 * Returns list of model attribute names that should affect downcast conversion.
65 */
66 getListAttributeNames(): Array<string>;
67 /**
68 * Attaches the listener to the {@link module:engine/view/document~Document#event:delete} event and handles backspace/delete
69 * keys in and around document lists.
70 */
71 private _setupDeleteIntegration;
72 /**
73 * Attaches a listener to the {@link module:engine/view/document~Document#event:enter} event and handles enter key press
74 * in document lists.
75 */
76 private _setupEnterIntegration;
77 /**
78 * Attaches a listener to the {@link module:engine/view/document~Document#event:tab} event and handles tab key and tab+shift keys
79 * presses in document lists.
80 */
81 private _setupTabIntegration;
82 /**
83 * Registers the conversion helpers for the document-list feature.
84 */
85 private _setupConversion;
86 /**
87 * Registers model post-fixers.
88 */
89 private _setupModelPostFixing;
90 /**
91 * Integrates the feature with the clipboard via {@link module:engine/model/model~Model#insertContent} and
92 * {@link module:engine/model/model~Model#getSelectedContent}.
93 */
94 private _setupClipboardIntegration;
95 /**
96 * Informs editor accessibility features about keystrokes brought by the plugin.
97 */
98 private _setupAccessibilityIntegration;
99}
100/**
101 * The attribute to attribute downcast strategy for UL, OL, LI elements.
102 */
103export interface AttributeDowncastStrategy {
104 /**
105 * The scope of the downcast (whether it applies to LI or OL/UL).
106 */
107 scope: 'list' | 'item';
108 /**
109 * The model attribute name.
110 */
111 attributeName: string;
112 /**
113 * Sets the property on the view element.
114 */
115 setAttributeOnDowncast(writer: DowncastWriter, value: unknown, element: ViewElement): void;
116}
117/**
118 * The custom marker downcast strategy.
119 */
120export interface ItemMarkerDowncastStrategy {
121 /**
122 * The scope of the downcast.
123 */
124 scope: 'itemMarker';
125 /**
126 * The model attribute name.
127 */
128 attributeName: string;
129 /**
130 * Creates a view element for a custom item marker.
131 */
132 createElement(writer: DowncastWriter, modelElement: Element, { dataPipeline }: {
133 dataPipeline?: boolean;
134 }): ViewElement | null;
135 /**
136 * Creates an AttributeElement to be used for wrapping a first block of a list item.
137 */
138 createWrapperElement?(writer: DowncastWriter, modelElement: Element, { dataPipeline }: {
139 dataPipeline?: boolean;
140 }): ViewAttributeElement;
141 /**
142 * Should return true if the given list block can be wrapped with the wrapper created by `createWrapperElement()`
143 * or only the marker element should be wrapped.
144 */
145 canWrapElement?(modelElement: Element): boolean;
146 /**
147 * Should return true if the custom marker can be injected into a given list block.
148 * Otherwise, custom marker view element is always injected before the block element.
149 */
150 canInjectMarkerIntoElement?(modelElement: Element): boolean;
151}
152/**
153 * The downcast strategy.
154 */
155export type DowncastStrategy = AttributeDowncastStrategy | ItemMarkerDowncastStrategy;
156/**
157 * Event fired on changes detected on the model list element to verify if the view representation of a list element
158 * is representing those attributes.
159 *
160 * It allows triggering a re-wrapping of a list item.
161 *
162 * @internal
163 * @eventName ~ListEditing#postFixer
164 * @param listHead The head element of a list.
165 * @param writer The writer to do changes with.
166 * @param seenIds The set of already known IDs.
167 * @returns If a post-fixer made a change of the model tree, it should return `true`.
168 */
169export type ListEditingPostFixerEvent = {
170 name: 'postFixer';
171 args: [
172 {
173 listNodes: ListBlocksIterable;
174 listHead: Element;
175 writer: Writer;
176 seenIds: Set<string>;
177 }
178 ];
179 return: boolean;
180};
181/**
182 * Event fired on changes detected on the model list element to verify if the view representation of a list element
183 * is representing those attributes.
184 *
185 * It allows triggering a re-wrapping of a list item.
186 *
187 * **Note**: For convenience this event is namespaced and could be captured as `checkAttributes:list` or `checkAttributes:item`.
188 *
189 * @internal
190 * @eventName ~ListEditing#checkAttributes
191 */
192export type ListEditingCheckAttributesEvent = {
193 name: 'checkAttributes' | 'checkAttributes:list' | 'checkAttributes:item';
194 args: [
195 {
196 viewElement: ViewElement & {
197 id?: string;
198 };
199 modelAttributes: ListItemAttributesMap;
200 }
201 ];
202 return: boolean;
203};
204/**
205 * Event fired on changes detected on the model list element to verify if the view representation of a list block element
206 * is representing those attributes.
207 *
208 * It allows triggering a reconversion of a list item block.
209 *
210 * @internal
211 * @eventName ~ListEditing#checkElement
212 */
213export type ListEditingCheckElementEvent = {
214 name: 'checkElement';
215 args: [
216 {
217 viewElement: ViewElement;
218 modelElement: Element;
219 }
220 ];
221 return: boolean;
222};