@alitons/ckeditor5 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/build/ckeditor.d.ts +7 -13
- package/build/ckeditor.js +2 -2
- package/build/ckeditor.js.map +1 -1
- package/build/components/list/listitemview.d.ts +15 -0
- package/build/components/list/listseparatorview.d.ts +9 -0
- package/build/components/list/listview.d.ts +25 -0
- package/build/plugins/NumberedDivListSplit.d.ts +6 -0
- package/build/plugins/listaNumerada-bkp.d.ts +5 -0
- package/build/plugins/listaNumerada.d.ts +5 -0
- package/package.json +3 -2
- package/sample/index.html +12 -2
- package/sample/script.js +44 -17
- package/src/ckeditor.ts +7 -2
- package/src/components/list/listitemview.d.ts +35 -0
- package/src/components/list/listitemview.js +42 -0
- package/src/components/list/listview.d.ts +65 -0
- package/src/components/list/listview.js +92 -0
- package/src/css/custom.css +87 -0
- package/src/plugins/NumberedDivListSplit.ts +174 -0
- package/src/plugins/listaNumerada.ts +603 -0
- package/src/plugins/modelo.ts +102 -102
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
import { Plugin, Command } from '@ckeditor/ckeditor5-core';
|
|
2
|
+
import { findOptimalInsertionRange } from '@ckeditor/ckeditor5-widget/src/utils';
|
|
3
|
+
import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
|
|
4
|
+
|
|
5
|
+
export default class NumberedDivList extends Plugin {
|
|
6
|
+
static get pluginName() {
|
|
7
|
+
return 'NumberedDivList';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
init() {
|
|
11
|
+
const editor = this.editor;
|
|
12
|
+
const toolbar = editor.config.get('toolbar') as any;
|
|
13
|
+
const config = toolbar!.listaNumeradaOptions || {};
|
|
14
|
+
|
|
15
|
+
editor.model.schema.register('numList', {
|
|
16
|
+
allowWhere: '$block',
|
|
17
|
+
allowContentOf: '$root',
|
|
18
|
+
allowAttributesOf: '$block',
|
|
19
|
+
isBlock: true,
|
|
20
|
+
isLimit: true,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
editor.model.schema.register('numItem', {
|
|
24
|
+
allowIn: 'numList',
|
|
25
|
+
allowContentOf: '$root',
|
|
26
|
+
allowAttributesOf: '$block',
|
|
27
|
+
isLimit: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const downcastAttr = (conv: any, key: 'dataStyle'|'dataBlock', viewKey: 'data-style'|'data-block') => {
|
|
31
|
+
conv.attributeToAttribute({
|
|
32
|
+
model: { name: 'numList', key },
|
|
33
|
+
view: (val: unknown) => {
|
|
34
|
+
if (val == null || val === '') return { key: viewKey, value: null };
|
|
35
|
+
return { key: viewKey, value: String(val) };
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
downcastAttr(editor.conversion.for('editingDowncast'), 'dataStyle', 'data-style');
|
|
40
|
+
downcastAttr(editor.conversion.for('dataDowncast'), 'dataStyle', 'data-style');
|
|
41
|
+
downcastAttr(editor.conversion.for('editingDowncast'), 'dataBlock', 'data-block');
|
|
42
|
+
downcastAttr(editor.conversion.for('dataDowncast'), 'dataBlock', 'data-block');
|
|
43
|
+
|
|
44
|
+
editor.conversion.for('dataDowncast').elementToElement({
|
|
45
|
+
model: 'numList',
|
|
46
|
+
view: (modelElement, { writer }) => {
|
|
47
|
+
const attrs: Record<string, string> = { class: 'num-list' };
|
|
48
|
+
|
|
49
|
+
const ds = modelElement.getAttribute('data-style');
|
|
50
|
+
if (ds != null) attrs['data-style'] = String(ds);
|
|
51
|
+
|
|
52
|
+
const db = modelElement.getAttribute('data-block');
|
|
53
|
+
if (db != null) attrs['data-block'] = String(db);
|
|
54
|
+
|
|
55
|
+
const sq = modelElement.getAttribute('start');
|
|
56
|
+
if (sq != null) attrs['start'] = String(sq);
|
|
57
|
+
|
|
58
|
+
return writer.createContainerElement('div', attrs)
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
editor.conversion.for('editingDowncast').elementToElement({
|
|
63
|
+
model: 'numList',
|
|
64
|
+
view: (modelElement, { writer }) => {
|
|
65
|
+
const attrs: Record<string, string> = { class: 'num-list' };
|
|
66
|
+
|
|
67
|
+
const ds = modelElement.getAttribute('data-style');
|
|
68
|
+
if (ds != null) attrs['data-style'] = String(ds);
|
|
69
|
+
|
|
70
|
+
const db = modelElement.getAttribute('data-block');
|
|
71
|
+
if (db != null) attrs['data-block'] = String(db);
|
|
72
|
+
|
|
73
|
+
const sq = modelElement.getAttribute('start');
|
|
74
|
+
if (sq != null) attrs['start'] = String(sq);
|
|
75
|
+
|
|
76
|
+
return writer.createContainerElement('div', attrs);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
editor.conversion.for('dataDowncast').elementToElement({
|
|
81
|
+
model: 'numItem',
|
|
82
|
+
view: (modelElement, { writer }) =>
|
|
83
|
+
writer.createContainerElement('div', { class: 'num-li' })
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
editor.conversion.for('editingDowncast').elementToElement({
|
|
87
|
+
model: 'numItem',
|
|
88
|
+
view: (modelElement, { writer }) =>
|
|
89
|
+
writer.createContainerElement('div', { class: 'num-li' })
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
editor.conversion.for('upcast').elementToElement({
|
|
93
|
+
view: {
|
|
94
|
+
name: 'div',
|
|
95
|
+
classes: 'num-list',
|
|
96
|
+
},
|
|
97
|
+
model: (viewElement, { writer }) => {
|
|
98
|
+
const attrs: any = {};
|
|
99
|
+
|
|
100
|
+
const ds = viewElement.getAttribute('data-style');
|
|
101
|
+
if (ds != null) attrs.dataStyle = ds;
|
|
102
|
+
|
|
103
|
+
const db = viewElement.getAttribute('data-block');
|
|
104
|
+
if (db != null) attrs.dataBlock = db;
|
|
105
|
+
|
|
106
|
+
const sq = viewElement.getAttribute('start');
|
|
107
|
+
if (sq != null) attrs.start = sq;
|
|
108
|
+
|
|
109
|
+
return writer.createElement('numList', attrs);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
editor.conversion.for('upcast').elementToElement({
|
|
114
|
+
view: {
|
|
115
|
+
name: 'div',
|
|
116
|
+
classes: 'num-li'
|
|
117
|
+
},
|
|
118
|
+
model: (viewElement, { writer }) => writer.createElement('numItem')
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
editor.conversion.for('upcast').elementToElement({
|
|
122
|
+
view: { name: 'ol' },
|
|
123
|
+
model: (viewElement, { writer }) => writer.createElement('numList')
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
editor.conversion.for('upcast').elementToElement({
|
|
127
|
+
view: { name: 'li' },
|
|
128
|
+
model: (viewElement, { writer }) => writer.createElement('numItem')
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const viewDoc = editor.editing.view.document;
|
|
132
|
+
|
|
133
|
+
const selectionSpansBlocks = (model: any) => {
|
|
134
|
+
const sel = model.document.selection;
|
|
135
|
+
if (sel.isCollapsed) return false;
|
|
136
|
+
const blocks = Array.from(sel.getSelectedBlocks());
|
|
137
|
+
return blocks.length > 1;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const selectionTouchesElements = (model: any) => {
|
|
141
|
+
const sel = model.document.selection;
|
|
142
|
+
if (sel.isCollapsed) return false;
|
|
143
|
+
const range = sel.getFirstRange();
|
|
144
|
+
for (const item of range.getItems()) {
|
|
145
|
+
if (item.is?.('element')) return true;
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
function getNearestSealedListFromPos(pos: any): any | null {
|
|
151
|
+
// ancestors: [root, ..., parent]
|
|
152
|
+
const ancestors = pos.getAncestors({ includeSelf: false }); // CKEditor retorna de root -> parent
|
|
153
|
+
// percorre de baixo pra cima (mais próximo primeiro)
|
|
154
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
155
|
+
const node = ancestors[i];
|
|
156
|
+
if (node.is?.('element', 'numList') && !!node.getAttribute?.('dataBlock')) {
|
|
157
|
+
return node;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Verifica se `node` está DENTRO do `list` (qualquer profundidade)
|
|
164
|
+
function isInside(node: any, ancestor: any): boolean {
|
|
165
|
+
let p = node;
|
|
166
|
+
while (p) {
|
|
167
|
+
if (p === ancestor) return true;
|
|
168
|
+
p = p.parent;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const firstItem = (list: any) => list.getChild(0);
|
|
174
|
+
const lastItem = (list: any) => list.getChild(list.childCount - 1);
|
|
175
|
+
|
|
176
|
+
function ensureTypablePosInItem(writer: any, item: any, atEnd = false) {
|
|
177
|
+
let p = null;
|
|
178
|
+
for (const c of item.getChildren()) {
|
|
179
|
+
if (c.is?.('element', 'paragraph')) { p = c; break; }
|
|
180
|
+
}
|
|
181
|
+
if (!p) {
|
|
182
|
+
p = writer.createElement('paragraph');
|
|
183
|
+
writer.insert(p, writer.createPositionAt(item, 0));
|
|
184
|
+
}
|
|
185
|
+
return atEnd
|
|
186
|
+
? writer.createPositionAt(p, 'end')
|
|
187
|
+
: writer.createPositionAt(p, 0);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
editor.plugins.get('ClipboardPipeline').on('inputTransformation', (evt: any, data: any) => {
|
|
191
|
+
const { model } = editor;
|
|
192
|
+
const sel = model.document.selection;
|
|
193
|
+
const pos = sel.getFirstPosition();
|
|
194
|
+
if (!pos) return;
|
|
195
|
+
|
|
196
|
+
const sealed = getNearestSealedListFromPos(pos);
|
|
197
|
+
if (!sealed) return;
|
|
198
|
+
|
|
199
|
+
const item = pos.findAncestor('numItem');
|
|
200
|
+
if (item && isInside(item, sealed)) return; // já dentro: ok
|
|
201
|
+
|
|
202
|
+
// redireciona para o primeiro item do sealed
|
|
203
|
+
evt.stop();
|
|
204
|
+
model.change((writer: any) => {
|
|
205
|
+
const target = firstItem(sealed) || writer.createElement('numItem');
|
|
206
|
+
if (!firstItem(sealed)) {
|
|
207
|
+
writer.insert(target, writer.createPositionAt(sealed, 0));
|
|
208
|
+
}
|
|
209
|
+
const at = ensureTypablePosInItem(writer, target, false);
|
|
210
|
+
writer.setSelection(at);
|
|
211
|
+
// reaplica como texto simples (ajuste conforme sua pipeline)
|
|
212
|
+
const text = data.dataTransfer.getData('text/plain');
|
|
213
|
+
if (text) editor.execute('input', { text });
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
// não permitir que saia da lista caso exista o atributo data-block
|
|
219
|
+
// viewDoc.on('keydown', (evt, data) => {
|
|
220
|
+
// if (data.keyCode !== keyCodes.enter || data.shiftKey) return;
|
|
221
|
+
// if (!editor.model.document.selection.isCollapsed) return;
|
|
222
|
+
|
|
223
|
+
// if(config?.disableEnter === true) {
|
|
224
|
+
// return;
|
|
225
|
+
// }
|
|
226
|
+
|
|
227
|
+
// const { model } = editor;
|
|
228
|
+
// const pos = model.document.selection.getFirstPosition();
|
|
229
|
+
// if (!pos) return;
|
|
230
|
+
|
|
231
|
+
// const sealed = getNearestSealedListFromPos(pos);
|
|
232
|
+
// if (!sealed) return; // só intercepta se estiver dentro de um sealed
|
|
233
|
+
|
|
234
|
+
// data.preventDefault(); evt.stop();
|
|
235
|
+
|
|
236
|
+
// model.change(writer => {
|
|
237
|
+
// // deixa o enter nativo dividir o bloco
|
|
238
|
+
// editor.execute('enter');
|
|
239
|
+
|
|
240
|
+
// const posAfter = model.document.selection.getFirstPosition();
|
|
241
|
+
// if (!posAfter) return;
|
|
242
|
+
// const newBlock = posAfter.parent as any;
|
|
243
|
+
// if (!newBlock) return;
|
|
244
|
+
|
|
245
|
+
// // queremos transformar o novo bloco em um novo numItem,
|
|
246
|
+
// // mantendo-o dentro de ALGUM numList que esteja dentro do `sealed` (o mais próximo)
|
|
247
|
+
// // pega o numItem atual (mais próximo)
|
|
248
|
+
// let currentItem = posAfter.findAncestor('numItem');
|
|
249
|
+
|
|
250
|
+
// // se o bloco recém criado ficou fora de um numItem,
|
|
251
|
+
// // cria um numItem irmão do atual (se existir) dentro do mesmo numList
|
|
252
|
+
// if (!currentItem) {
|
|
253
|
+
// const listForNew = getNearestSealedListFromPos(posAfter) || sealed;
|
|
254
|
+
// const newItem = writer.createElement('numItem');
|
|
255
|
+
// writer.insert(newItem, writer.createPositionAt(listForNew, 'end'));
|
|
256
|
+
// writer.move(writer.createRangeOn(newBlock), writer.createPositionAt(newItem, 0));
|
|
257
|
+
// writer.setSelection(ensureTypablePosInItem(writer, newItem, false));
|
|
258
|
+
// return;
|
|
259
|
+
// }
|
|
260
|
+
|
|
261
|
+
// // garantir que este numItem pertence a um numList que está dentro do sealed mais próximo
|
|
262
|
+
// let itsList = currentItem.parent; // deve ser um numList
|
|
263
|
+
// if (!isInside(itsList, sealed)) {
|
|
264
|
+
// // se por alguma razão o split empurrou pra fora, anexa de volta ao sealed
|
|
265
|
+
// const fallback = firstItem(sealed) || null;
|
|
266
|
+
// if (fallback) {
|
|
267
|
+
// const newItem = writer.createElement('numItem');
|
|
268
|
+
// writer.insert(newItem, writer.createPositionAfter(fallback));
|
|
269
|
+
// writer.move(writer.createRangeOn(newBlock), writer.createPositionAt(newItem, 0));
|
|
270
|
+
// writer.setSelection(ensureTypablePosInItem(writer, newItem, false));
|
|
271
|
+
// } else {
|
|
272
|
+
// // sealed vazio (raro): crie o primeiro item
|
|
273
|
+
// const newItem = writer.createElement('numItem');
|
|
274
|
+
// writer.insert(newItem, writer.createPositionAt(sealed, 0));
|
|
275
|
+
// writer.move(writer.createRangeOn(newBlock), writer.createPositionAt(newItem, 0));
|
|
276
|
+
// writer.setSelection(ensureTypablePosInItem(writer, newItem, false));
|
|
277
|
+
// }
|
|
278
|
+
// return;
|
|
279
|
+
// }
|
|
280
|
+
|
|
281
|
+
// // caso normal: cria irmão dentro do mesmo numList
|
|
282
|
+
// const newItem = writer.createElement('numItem');
|
|
283
|
+
// writer.insert(newItem, writer.createPositionAfter(currentItem));
|
|
284
|
+
// writer.move(writer.createRangeOn(newBlock), writer.createPositionAt(newItem, 0));
|
|
285
|
+
// writer.setSelection(ensureTypablePosInItem(writer, newItem, false));
|
|
286
|
+
// });
|
|
287
|
+
// }, { priority: 'high' });
|
|
288
|
+
|
|
289
|
+
// não permitir que backspace/delete saia da lista caso exista o atributo data-block
|
|
290
|
+
viewDoc.on(
|
|
291
|
+
'keydown',
|
|
292
|
+
(evt, data) => {
|
|
293
|
+
const isBackspace = data.keyCode === keyCodes.backspace;
|
|
294
|
+
const isDelete = data.keyCode === keyCodes.delete;
|
|
295
|
+
if (!isBackspace && !isDelete) return;
|
|
296
|
+
if (!editor.model.document.selection.isCollapsed) return;
|
|
297
|
+
|
|
298
|
+
const model = editor.model;
|
|
299
|
+
const sel = model.document.selection;
|
|
300
|
+
|
|
301
|
+
data.preventDefault();
|
|
302
|
+
evt.stop();
|
|
303
|
+
|
|
304
|
+
model.change( async (writer: any) => {
|
|
305
|
+
// @ts-ignore
|
|
306
|
+
const getPos = sel.getFirstPosition() as any;
|
|
307
|
+
const blocoPos = getPos?.findAncestor('paragraph') ?? getPos?.findAncestor('numItem') ?? getPos?.findAncestor('numList') ?? null;
|
|
308
|
+
const itemPos = blocoPos.findAncestor('numItem');
|
|
309
|
+
const listPos = itemPos.findAncestor('numList');
|
|
310
|
+
|
|
311
|
+
// @ts-ignore
|
|
312
|
+
editor.execute(isBackspace ? 'delete' : 'deleteForward', { unit: 'character', direction: isBackspace ? 'backward' : 'forward' });
|
|
313
|
+
|
|
314
|
+
if(blocoPos.is('element', 'paragraph') && !blocoPos?.getChild(0)) {
|
|
315
|
+
writer.remove(blocoPos);
|
|
316
|
+
}
|
|
317
|
+
if(itemPos && itemPos.childCount === 0) {
|
|
318
|
+
writer.remove(itemPos);
|
|
319
|
+
}
|
|
320
|
+
if(listPos && listPos.childCount === 0) {
|
|
321
|
+
writer.remove(listPos);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
if(isBackspace) {
|
|
328
|
+
model.change( async (writer: any) => {
|
|
329
|
+
// coloca o cursor no final do bloco atual
|
|
330
|
+
const posAfter = model.document.selection.getFirstPosition() as any;
|
|
331
|
+
writer.setSelection(writer.createPositionAt(posAfter.parent, 'end'));
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return;
|
|
336
|
+
|
|
337
|
+
},
|
|
338
|
+
{ priority: 'high' }
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
/* Ação ao pressionar a tecla TAB */
|
|
342
|
+
viewDoc.on(
|
|
343
|
+
'keydown',
|
|
344
|
+
(evt, data) => {
|
|
345
|
+
if (!(data.keyCode == keyCodes.tab && !data.shiftKey)) return;
|
|
346
|
+
if (!editor.model.document.selection.isCollapsed) return;
|
|
347
|
+
|
|
348
|
+
const model = editor.model;
|
|
349
|
+
|
|
350
|
+
const posBefore = model.document.selection.getFirstPosition();
|
|
351
|
+
const inItem = posBefore?.findAncestor('numItem');
|
|
352
|
+
const inList = posBefore?.findAncestor('numList');
|
|
353
|
+
if (!inItem || !inList) return; // fora da nossa lista -> deixa o Tab padrão agir
|
|
354
|
+
|
|
355
|
+
data.preventDefault();
|
|
356
|
+
evt.stop();
|
|
357
|
+
|
|
358
|
+
editor.execute('toggleNumberedDivList');
|
|
359
|
+
},
|
|
360
|
+
{ priority: 'high' }
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
/* Ação ao pressionar as teclas Shift + Tab */
|
|
364
|
+
viewDoc.on(
|
|
365
|
+
'keydown',
|
|
366
|
+
(evt, data) => {
|
|
367
|
+
if (data.keyCode !== keyCodes.tab || !data.shiftKey) return;
|
|
368
|
+
if (!editor.model.document.selection.isCollapsed) return;
|
|
369
|
+
|
|
370
|
+
const posBefore = editor.model.document.selection.getFirstPosition();
|
|
371
|
+
const inItem = posBefore?.findAncestor('numItem');
|
|
372
|
+
const inList = posBefore?.findAncestor('numList');
|
|
373
|
+
if (!inItem || !inList) return;
|
|
374
|
+
|
|
375
|
+
data.preventDefault();
|
|
376
|
+
evt.stop();
|
|
377
|
+
|
|
378
|
+
shiftTab(editor);
|
|
379
|
+
|
|
380
|
+
},
|
|
381
|
+
{ priority: 'high' }
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
editor.model.document.on('change:data', () => {
|
|
386
|
+
executeForceList(editor, config);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
editor.model.schema.extend('$text', {
|
|
391
|
+
allowAttributes: ['dataBlock', 'start']
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// quando o editor for inicializado, dispara o change:data uma vez
|
|
395
|
+
editor.on('ready', () => {
|
|
396
|
+
executeForceList(editor, config);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
// @ts-ignore
|
|
401
|
+
editor.commands.add('toggleNumberedDivList', new ToggleNumberedDivListCommand(editor));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
class ToggleNumberedDivListCommand {
|
|
406
|
+
declare editor: any;
|
|
407
|
+
declare value: boolean;
|
|
408
|
+
declare isEnabled: boolean;
|
|
409
|
+
|
|
410
|
+
constructor(editor: any) {
|
|
411
|
+
this.editor = editor;
|
|
412
|
+
this.value = false;
|
|
413
|
+
this.isEnabled = true;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
refresh() {
|
|
417
|
+
this.isEnabled = true;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
execute(options?: { value?: string; start?: number } ): void {
|
|
421
|
+
const { value, start } = options || {};
|
|
422
|
+
const editor = this?.editor || null;
|
|
423
|
+
const model = editor.model;
|
|
424
|
+
|
|
425
|
+
const selection = model.document.selection;
|
|
426
|
+
const firstPos = selection.getFirstPosition('paragraph') ?? selection.getFirstPosition('numItem');
|
|
427
|
+
const existingItem = firstPos?.findAncestor('numItem');
|
|
428
|
+
const paragraphAbove = firstPos?.findAncestor('paragraph');
|
|
429
|
+
const firstParagraphItem = existingItem ? existingItem?.getChild(0) : null;
|
|
430
|
+
let existingList = firstPos?.findAncestor('numList');
|
|
431
|
+
let firstItemInList = existingList ? existingList.getChild(0) : null;
|
|
432
|
+
const selectedItemIndex = existingList ? existingList.getChildIndex(existingItem) : null;
|
|
433
|
+
|
|
434
|
+
if(value === 'recuar') {
|
|
435
|
+
if (!existingItem || !existingList) return;
|
|
436
|
+
if(value === 'recuar') return shiftTab(editor);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if(value !== undefined) {
|
|
440
|
+
const block = firstPos.parent as any;
|
|
441
|
+
|
|
442
|
+
if(existingItem && firstItemInList === existingItem) {
|
|
443
|
+
if(value === 'decimal') {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if(existingItem?.getChild(0) === block) {
|
|
449
|
+
return editor.execute('toggleNumberedDivList', { value: undefined, start: start });
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
model.change((writer: any) => {
|
|
454
|
+
// se não for o primeiro do item
|
|
455
|
+
if ((value == undefined || value == 'decimal') && paragraphAbove === firstParagraphItem && selectedItemIndex >= 0) {
|
|
456
|
+
|
|
457
|
+
if(!selectedItemIndex) return;
|
|
458
|
+
|
|
459
|
+
// procura o numItem acima da seleção
|
|
460
|
+
let prevItem = existingList.getChild(selectedItemIndex - 1);
|
|
461
|
+
if (!prevItem) return;
|
|
462
|
+
|
|
463
|
+
if(prevItem.is('element', 'numItem')) {
|
|
464
|
+
// criar uma nova lista abaixo da prevItem
|
|
465
|
+
const newBlock = writer.createElement('numList', {
|
|
466
|
+
...(value && value !== 'decimal' ? { 'data-style': value } : {})
|
|
467
|
+
});
|
|
468
|
+
const insertPos = writer.createPositionAfter(prevItem);
|
|
469
|
+
writer.insert(newBlock, insertPos);
|
|
470
|
+
|
|
471
|
+
writer.move(writer.createRangeOn(existingItem), writer.createPositionAt(newBlock, 0));
|
|
472
|
+
} else {
|
|
473
|
+
// move para o final do prevItem
|
|
474
|
+
writer.move(writer.createRangeOn(existingItem), writer.createPositionAt(prevItem, 'end'));
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const blocks = Array.from(selection.getSelectedBlocks()) as any[];
|
|
481
|
+
|
|
482
|
+
if (!blocks.length) {
|
|
483
|
+
const insertRange = findOptimalInsertionRange(selection, model);
|
|
484
|
+
const numList = writer.createElement('numList');
|
|
485
|
+
const numItem = writer.createElement('numItem');
|
|
486
|
+
writer.insert(numList, insertRange.start);
|
|
487
|
+
writer.insert(numItem, writer.createPositionAt(numList, 0));
|
|
488
|
+
const paragraph = writer.createElement('paragraph');
|
|
489
|
+
writer.insert(paragraph, writer.createPositionAt(numItem, 0));
|
|
490
|
+
writer.setSelection(paragraph, 'in');
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (existingList && value === undefined) {
|
|
495
|
+
// verifica se está dentro de um numItem
|
|
496
|
+
const isInsideNumItem = blocks[0].findAncestor('numItem') === existingItem;
|
|
497
|
+
let insertPos = writer.createPositionBefore(paragraphAbove);
|
|
498
|
+
let numItem;
|
|
499
|
+
|
|
500
|
+
if (isInsideNumItem && existingItem) {
|
|
501
|
+
// cria um num item abaixo do existente
|
|
502
|
+
numItem = writer.createElement('numItem');
|
|
503
|
+
insertPos = writer.createPositionAfter(existingItem);
|
|
504
|
+
writer.insert(numItem, insertPos);
|
|
505
|
+
} else {
|
|
506
|
+
numItem = writer.createElement('numItem');
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
for (const block of blocks) {
|
|
510
|
+
writer.insert(numItem, insertPos);
|
|
511
|
+
writer.move(writer.createRangeOn(block), writer.createPositionAt(numItem, 0));
|
|
512
|
+
}
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// verifica se existe algum numList com data-first dentro de todo o conteúdo selecionado
|
|
517
|
+
let first = true;
|
|
518
|
+
for (const block of blocks) {
|
|
519
|
+
// @ts-ignore
|
|
520
|
+
const foundList = block.findAncestor('numList');
|
|
521
|
+
if (foundList) {
|
|
522
|
+
first = false;
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const firstBlock = blocks[0];
|
|
528
|
+
const listPos = writer.createPositionBefore(firstBlock);
|
|
529
|
+
const numList = writer.createElement('numList', {
|
|
530
|
+
...(first && !value ? { 'data-block': 'true' } : {}),
|
|
531
|
+
...(first && !start ? { 'start': start } : {}),
|
|
532
|
+
...((value && value !== 'recuar' && value !== 'decimal') ? { 'data-style': value } : {})
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
writer.insert(numList, listPos);
|
|
536
|
+
|
|
537
|
+
for (const block of blocks) {
|
|
538
|
+
const numItem = writer.createElement('numItem');
|
|
539
|
+
writer.insert(numItem, writer.createPositionAt(numList, 'end'));
|
|
540
|
+
writer.move(writer.createRangeOn(block), writer.createPositionAt(numItem, 0));
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function shiftTab(editor: any) {
|
|
547
|
+
const model = editor.model;
|
|
548
|
+
|
|
549
|
+
const selection = model.document.selection;
|
|
550
|
+
const firstBlock = selection.getFirstPosition('paragraph') ?? selection.getFirstPosition('numItem');
|
|
551
|
+
const firstPos = firstBlock.is('element', 'numItem') ? firstBlock : firstBlock.findAncestor('numItem');
|
|
552
|
+
const existingList = firstPos?.findAncestor('numList');
|
|
553
|
+
const selectedItemIndex = existingList ? existingList.getChildIndex(firstPos) : null;
|
|
554
|
+
const numListPai = existingList ? existingList?.findAncestor('numList') : null;
|
|
555
|
+
const selectedListPaiIndex = numListPai ? numListPai.getChildIndex(existingList) : null;
|
|
556
|
+
|
|
557
|
+
model.change((writer: any) => {
|
|
558
|
+
// se for o primeiro item da lista
|
|
559
|
+
if(numListPai && selectedListPaiIndex >= 0 && selectedItemIndex === 0) {
|
|
560
|
+
writer.move(writer.createRangeOn(firstPos), writer.createPositionAt(numListPai, selectedListPaiIndex));
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
// se for o último item da lista
|
|
564
|
+
if(numListPai && selectedListPaiIndex >= 0 && selectedItemIndex === existingList.childCount -1) {
|
|
565
|
+
writer.move(writer.createRangeOn(firstPos), writer.createPositionAt(numListPai, selectedListPaiIndex + 1));
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// criar novas regras de acordo com a necessidade
|
|
570
|
+
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function executeForceList(editor: any, config: any) {
|
|
575
|
+
const model = editor.model;
|
|
576
|
+
const firstElement = editor.model.document.getRoot().getChild(0) as any;
|
|
577
|
+
if(config?.forceList && firstElement && firstElement.name !== 'numList') {
|
|
578
|
+
const numList = model.change( (writer: any) => {
|
|
579
|
+
const insertPos = writer.createPositionAt(model.document.getRoot(), 0);
|
|
580
|
+
const numList = writer.createElement('numList', {
|
|
581
|
+
'data-block': 'true',
|
|
582
|
+
'start': config?.forceList ? config.forceList + 1 : null
|
|
583
|
+
});
|
|
584
|
+
writer.insert(numList, insertPos);
|
|
585
|
+
return numList;
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
model.change( (writer: any) => {
|
|
589
|
+
const root = model.document.getRoot();
|
|
590
|
+
const itemsToMove = [];
|
|
591
|
+
for ( const child of root.getChildren() ) {
|
|
592
|
+
if ( child !== numList ) {
|
|
593
|
+
itemsToMove.push(child);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
for ( const item of itemsToMove ) {
|
|
597
|
+
const numItem = writer.createElement('numList');
|
|
598
|
+
writer.insert( numItem, writer.createPositionAt( numList, 'end' ) );
|
|
599
|
+
writer.move( writer.createRangeOn( item ), writer.createPositionAt( numItem, 0 ) );
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|