@phuong-tran-redoc/document-engine-core 0.1.0 → 0.1.2

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.
Files changed (46) hide show
  1. package/README.md +105 -115
  2. package/THIRD-PARTY-NOTICES.txt +67 -0
  3. package/index.d.ts +1 -0
  4. package/index.esm.js +2238 -0
  5. package/package.json +21 -5
  6. package/src/__tests__/helpers/editor-factory.d.ts +0 -13
  7. package/src/__tests__/helpers/editor-factory.js +0 -25
  8. package/src/constants/indent.constant.js +0 -1
  9. package/src/constants/index.js +0 -1
  10. package/src/constants/table.constant.js +0 -1
  11. package/src/extensions/clear-content.extension.js +0 -9
  12. package/src/extensions/indent.extension.js +0 -59
  13. package/src/extensions/index.js +0 -8
  14. package/src/extensions/ordered-list.extension.js +0 -47
  15. package/src/extensions/reset-format.extension.js +0 -9
  16. package/src/extensions/reset-on-enter.extension.js +0 -37
  17. package/src/extensions/restricted-editing.extension.js +0 -376
  18. package/src/extensions/table-resizing.extension.js +0 -164
  19. package/src/extensions/table-style.extension.js +0 -481
  20. package/src/extensions/text-case.extension.js +0 -39
  21. package/src/index.js +0 -9
  22. package/src/kit/default-extensions.js +0 -86
  23. package/src/kit/generate-html.js +0 -40
  24. package/src/kit/index.js +0 -2
  25. package/src/migrations/doc-migrations.js +0 -24
  26. package/src/migrations/index.js +0 -3
  27. package/src/migrations/migrate-doc.js +0 -32
  28. package/src/migrations/types.js +0 -4
  29. package/src/models/color.model.js +0 -30
  30. package/src/models/index.js +0 -1
  31. package/src/nodes/dynamic-field.node.js +0 -141
  32. package/src/nodes/heading.node.js +0 -13
  33. package/src/nodes/image-ref.node.js +0 -138
  34. package/src/nodes/index.js +0 -4
  35. package/src/nodes/page-break.node.js +0 -52
  36. package/src/types/index.js +0 -2
  37. package/src/types/ordered-list.type.js +0 -2
  38. package/src/types/text-case.type.js +0 -1
  39. package/src/types/tiptap-html-server.d.js +0 -10
  40. package/src/utils/color.util.js +0 -31
  41. package/src/utils/dom.util.js +0 -33
  42. package/src/utils/index.js +0 -4
  43. package/src/utils/table.util.js +0 -95
  44. package/src/utils/text.util.js +0 -46
  45. package/src/views/block-handler.js +0 -311
  46. package/src/views/index.js +0 -1
@@ -1,33 +0,0 @@
1
- export function getClosestDomElement(editor, selector) {
2
- const { view, state } = editor;
3
- const { selection } = state;
4
- const { from } = selection;
5
- if (!from) return null;
6
- let domNodeAtPos = null;
7
- // Ưu tiên tìm DOM node của 'nearest parent element' của vị trí from
8
- // Nếu from là trong text, $from.parent.nodeDOM có thể chính xác hơn
9
- try {
10
- const resolvedPos = state.doc.resolve(from);
11
- domNodeAtPos = view.domAtPos(resolvedPos.pos).node; // Bắt đầu từ node ở vị trí from
12
- // Nếu domNodeAtPos không phải là HTMLElement (ví dụ, là một Text node),
13
- // thì cố gắng lấy element cha của nó trong DOM
14
- if (domNodeAtPos && !(domNodeAtPos instanceof HTMLElement)) {
15
- domNodeAtPos = domNodeAtPos.parentElement;
16
- }
17
- } catch (e) {
18
- // Xử lý trường hợp hiếm gặp khi nodeDOM(from) không trả về gì hợp lệ
19
- console.warn('Could not find DOM node at cursor position:', e);
20
- return null;
21
- }
22
- if (!domNodeAtPos) return null;
23
- const boundary = editor.view.dom;
24
- // Tìm kiếm từ domNodeAtPos lên trên cho đến khi gặp editor container
25
- let currentElement = domNodeAtPos;
26
- while(currentElement && currentElement !== boundary){
27
- if (currentElement.matches(selector)) {
28
- return currentElement;
29
- }
30
- currentElement = currentElement.parentElement;
31
- }
32
- return null;
33
- }
@@ -1,4 +0,0 @@
1
- export * from './color.util';
2
- export * from './dom.util';
3
- export * from './table.util';
4
- export * from './text.util';
@@ -1,95 +0,0 @@
1
- import { CellSelection, findTable, selectionCell, TableMap } from '@tiptap/pm/tables';
2
- import { isEqual } from 'lodash-es';
3
- export function getCursorCellInfo(state) {
4
- // 1. Dùng selectionCell để tìm vị trí bắt đầu của ô chứa con trỏ
5
- try {
6
- const cellInfo = selectionCell(state);
7
- // Nếu không tìm thấy (con trỏ không nằm trong ô nào), trả về null
8
- if (!cellInfo) return null;
9
- // cellInfo.$pos là một ResolvedPos trỏ tới vị trí bắt đầu của ô
10
- const table = findTable(cellInfo);
11
- if (!table) return null;
12
- const map = TableMap.get(table.node);
13
- // 2. Lấy vị trí tương đối của ô so với bảng
14
- const cellOffset = cellInfo.pos - table.start;
15
- // 3. Tìm chỉ số (index) của ô này trong "bản đồ" của bảng
16
- const cellIndex = map.map.indexOf(cellOffset);
17
- if (cellIndex === -1) {
18
- return null;
19
- }
20
- // 4. Từ chỉ số, tính ra rowIndex và colIndex
21
- const rowIndex = Math.floor(cellIndex / map.width);
22
- const colIndex = cellIndex % map.width;
23
- return {
24
- rowIndex,
25
- colIndex,
26
- cellIndex,
27
- tableStart: table.start,
28
- tableNode: table.node,
29
- map
30
- };
31
- } catch {
32
- // selectionCell throws RangeError when cursor is not in a table
33
- return null;
34
- }
35
- }
36
- export function getSelectedCells(state) {
37
- const { selection } = state;
38
- const cells = [];
39
- // Trường hợp chọn nhiều ô
40
- if (selection instanceof CellSelection) {
41
- selection.forEachCell((node, pos)=>cells.push({
42
- pos,
43
- node
44
- }));
45
- return cells;
46
- }
47
- // Trường hợp con trỏ nằm trong một ô
48
- if (selection.empty) {
49
- // Tìm node cell cha gần nhất
50
- const cell = selection.$head.node(-1);
51
- if (!cell || !(cell.type.name === 'tableCell' || cell.type.name === 'tableHeader')) return cells;
52
- // Lấy vị trí bắt đầu của node cell
53
- const cellPos = selection.$head.start(-1);
54
- cells.push({
55
- pos: cellPos,
56
- node: cell
57
- });
58
- }
59
- return cells;
60
- }
61
- /**
62
- * Quyết định giá trị thuộc tính cuối cùng từ một danh sách các cell đã chọn.
63
- * @param cells Danh sách các cell được chọn.
64
- * @param attributeName Tên của thuộc tính cần lấy (ví dụ: 'backgroundColor').
65
- * @returns Giá trị chung, hoặc MIXED_VALUE nếu các giá trị không đồng nhất.
66
- */ export function getCombinedCellAttributeValue(cells, attributeName, fallbackValue) {
67
- // Không có cell nào được chọn
68
- if (cells.length === 0) return fallbackValue;
69
- const firstValue = cells[0].node.attrs[attributeName];
70
- for(let i = 1; i < cells.length; i++){
71
- const currentValue = cells[i].node.attrs[attributeName];
72
- // Dùng isEqual để so sánh object (quan trọng cho thuộc tính 'border')
73
- if (!isEqual(currentValue, firstValue)) return undefined;
74
- }
75
- return firstValue;
76
- }
77
- /**
78
- * Get the actual table DOM element from a node position
79
- * Handles both direct table elements and NodeView wrappers
80
- * Uses :scope selector to avoid selecting nested tables
81
- *
82
- * @export For use in other table-related extensions
83
- */ export function getTableDOMFromView(view, tablePos) {
84
- const nodeDOMEl = view.nodeDOM(tablePos);
85
- if (!nodeDOMEl) return null;
86
- // Case 1: nodeDOMEl is the table itself
87
- if (nodeDOMEl.tagName === 'TABLE') {
88
- return nodeDOMEl;
89
- }
90
- // Case 2: nodeDOMEl is a NodeView wrapper (figure.widget)
91
- // Structure: figure.widget > table.widget-content
92
- // Use :scope to only get direct child, avoiding nested tables in cells
93
- const tableDOM = nodeDOMEl.querySelector(':scope > table.widget-content');
94
- return tableDOM;
95
- }
@@ -1,46 +0,0 @@
1
- import { TextSelection } from '@tiptap/pm/state';
2
- export function getSelectedText(editor) {
3
- const { state } = editor;
4
- const { selection } = state;
5
- // 1. Kiểm tra nếu selection rỗng (chỉ là con trỏ) hoặc không phải là TextSelection
6
- if (selection.empty || !(selection instanceof TextSelection)) {
7
- return null;
8
- }
9
- // 2. Lấy text giữa điểm đầu và điểm cuối của selection
10
- const { from, to } = selection;
11
- return state.doc.textBetween(from, to, ' '); // Trả về text
12
- }
13
- /**
14
- * Tìm instance và range (from, to) của một mark cụ thể tại vị trí con trỏ.
15
- * @param editor The Tiptap editor instance.
16
- * @param markName Tên của mark cần tìm (vd: 'link').
17
- * @returns Object chứa from, to, và mark instance nếu tìm thấy, ngược lại trả về null.
18
- */ export function getActiveMarkRange(editor, markName) {
19
- const { state } = editor;
20
- const { $from } = state.selection;
21
- const { doc } = state;
22
- // 2. Lấy instance của mark tại vị trí con trỏ
23
- const activeMarks = $from.marks();
24
- const targetMark = activeMarks.find((mark)=>mark.type.name === markName);
25
- // Nếu không có mark active, thoát ngay
26
- if (!targetMark) return null;
27
- const markType = targetMark.type;
28
- // 3. Quét ngược về đầu để tìm vị trí `from`
29
- let from = $from.pos;
30
- // Giới hạn việc quét trong node cha hiện tại ($from.start())
31
- while(from > $from.start() && doc.rangeHasMark(from - 1, from, markType)){
32
- from--;
33
- }
34
- // 4. Quét xuôi về cuối để tìm vị trí `to`
35
- let to = $from.pos;
36
- // Giới hạn việc quét trong node cha hiện tại ($from.end())
37
- while(to < $from.end() && doc.rangeHasMark(to, to + 1, markType)){
38
- to++;
39
- }
40
- // 5. Trả về kết quả
41
- return {
42
- from,
43
- to,
44
- mark: targetMark
45
- };
46
- }
@@ -1,311 +0,0 @@
1
- import { DOMSerializer } from '@tiptap/pm/model';
2
- import { NodeSelection, TextSelection } from '@tiptap/pm/state';
3
- export class HandleNodeView {
4
- setupDragHandling(view, getPos, node) {
5
- const handleDragStart = (event)=>{
6
- const pos = getPos();
7
- if (pos === undefined || !event.dataTransfer) return;
8
- // Let ProseMirror know which node is being dragged
9
- // Store node position and allow move effect
10
- event.dataTransfer.effectAllowed = 'move';
11
- // ProseMirror expects this format
12
- const slice = view.state.doc.slice(pos, pos + node.nodeSize);
13
- const serializer = DOMSerializer.fromSchema(view.state.schema);
14
- const dom = document.createElement('div');
15
- dom.appendChild(serializer.serializeFragment(slice.content));
16
- event.dataTransfer.setDragImage(this.dom, 0, 0);
17
- event.dataTransfer.setData('text/html', dom.innerHTML);
18
- // Add dragging state
19
- this.dom.classList.add('widget-dragging');
20
- };
21
- const handleDragEnd = ()=>{
22
- this.dom.classList.remove('widget-dragging');
23
- };
24
- this.dom.addEventListener('dragstart', handleDragStart);
25
- this.dom.addEventListener('dragend', handleDragEnd);
26
- this.eventCleanups.push(()=>{
27
- this.dom.removeEventListener('dragstart', handleDragStart);
28
- this.dom.removeEventListener('dragend', handleDragEnd);
29
- });
30
- }
31
- createBlockHandle(view, getPos) {
32
- const handle = document.createElement('div');
33
- handle.className = 'widget__selection-handle';
34
- handle.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="24px" fill="currentColor" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M360-160q-33 0-56.5-23.5T280-240q0-33 23.5-56.5T360-320q33 0 56.5 23.5T440-240q0 33-23.5 56.5T360-160Zm240 0q-33 0-56.5-23.5T520-240q0-33 23.5-56.5T600-320q33 0 56.5 23.5T680-240q0 33-23.5 56.5T600-160ZM360-400q-33 0-56.5-23.5T280-480q0-33 23.5-56.5T360-560q33 0 56.5 23.5T440-480q0 33-23.5 56.5T360-400Zm240 0q-33 0-56.5-23.5T520-480q0-33 23.5-56.5T600-560q33 0 56.5 23.5T680-480q0 33-23.5 56.5T600-400ZM360-640q-33 0-56.5-23.5T280-720q0-33 23.5-56.5T360-800q33 0 56.5 23.5T440-720q0 33-23.5 56.5T360-640Zm240 0q-33 0-56.5-23.5T520-720q0-33 23.5-56.5T600-800q33 0 56.5 23.5T680-720q0 33-23.5 56.5T600-640Z"/></svg>';
35
- handle.contentEditable = 'false';
36
- // handle.draggable = true;
37
- handle.setAttribute('data-drag-handle', '');
38
- handle.setAttribute('data-block-handle', ''); // Mark để dễ check trong stopEvent
39
- // Logic click-to-select (only on click, not drag)
40
- let isDragging = false;
41
- const handleMouseDown = ()=>{
42
- isDragging = false;
43
- // Let drag happen naturally, don't prevent
44
- };
45
- const handleDragStart = ()=>{
46
- isDragging = true;
47
- };
48
- const handleClick = (event)=>{
49
- // Only select if it was a click, not a drag
50
- if (!isDragging && event.button === 0) {
51
- event.preventDefault();
52
- event.stopPropagation();
53
- const pos = getPos() ?? 0;
54
- const tr = view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos));
55
- view.dispatch(tr);
56
- }
57
- isDragging = false;
58
- };
59
- handle.addEventListener('mousedown', handleMouseDown);
60
- // handle.addEventListener('dragstart', handleDragStart);
61
- handle.addEventListener('click', handleClick);
62
- // Store cleanup
63
- this.eventCleanups.push(()=>{
64
- handle.removeEventListener('mousedown', handleMouseDown);
65
- // handle.removeEventListener('dragstart', handleDragStart);
66
- handle.removeEventListener('click', handleClick);
67
- });
68
- return handle;
69
- }
70
- createTypeAround(view, getPos, node) {
71
- const wrapper = document.createElement('div');
72
- wrapper.className = 'type-around';
73
- const icon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/></svg>';
74
- const buttonBefore = document.createElement('div');
75
- buttonBefore.className = 'widget__type-around__button widget__type-around__button_before';
76
- buttonBefore.title = 'Insert paragraph before';
77
- buttonBefore.innerHTML = icon;
78
- const buttonAfter = document.createElement('div');
79
- buttonAfter.className = 'widget__type-around__button widget__type-around__button_after';
80
- buttonAfter.title = 'Insert paragraph after';
81
- buttonAfter.innerHTML = icon;
82
- // Logic chèn node 'paragraph' mới
83
- const handleInsert = (position)=>{
84
- const currentPos = getPos();
85
- if (currentPos === undefined) return;
86
- const newNode = view.state.schema.nodes['paragraph'].create();
87
- let insertPos;
88
- if (position === 'before') {
89
- insertPos = currentPos;
90
- } else {
91
- const tableStartPos = view.state.doc.resolve(currentPos);
92
- const tableIndex = tableStartPos.index();
93
- insertPos = tableStartPos.posAtIndex(tableIndex + 1);
94
- }
95
- const tr = view.state.tr.insert(insertPos, newNode);
96
- const mappedInsertPos = tr.mapping.map(insertPos, -1);
97
- const selectionPos = mappedInsertPos + 1;
98
- // Di chuyển con trỏ vào node mới
99
- view.dispatch(tr.setSelection(TextSelection.create(tr.doc, selectionPos)));
100
- view.focus();
101
- };
102
- // Button Before event handlers
103
- const beforeMouseDown = (e)=>{
104
- e.preventDefault();
105
- e.stopPropagation();
106
- };
107
- const beforeClick = (e)=>{
108
- e.preventDefault();
109
- e.stopPropagation();
110
- handleInsert('before');
111
- };
112
- buttonBefore.addEventListener('mousedown', beforeMouseDown);
113
- buttonBefore.addEventListener('click', beforeClick);
114
- // Button After event handlers
115
- const afterMouseDown = (e)=>{
116
- e.preventDefault();
117
- e.stopPropagation();
118
- };
119
- const afterClick = (e)=>{
120
- e.preventDefault();
121
- e.stopPropagation();
122
- handleInsert('after');
123
- };
124
- buttonAfter.addEventListener('mousedown', afterMouseDown);
125
- buttonAfter.addEventListener('click', afterClick);
126
- // Store cleanup functions
127
- this.eventCleanups.push(()=>{
128
- buttonBefore.removeEventListener('mousedown', beforeMouseDown);
129
- buttonBefore.removeEventListener('click', beforeClick);
130
- buttonAfter.removeEventListener('mousedown', afterMouseDown);
131
- buttonAfter.removeEventListener('click', afterClick);
132
- });
133
- wrapper.append(buttonBefore, buttonAfter);
134
- return wrapper;
135
- }
136
- /**
137
- * Ngăn các sự kiện từ handle và type-around buttons lan truyền vào editor
138
- * CHỈ chặn events từ các controls, KHÔNG chặn events từ content
139
- *
140
- * QUAN TRỌNG:
141
- * - return true = ProseMirror BỎ QUA event này
142
- * - return false = ProseMirror XỬ LÝ event này (table editing, selection, drag, etc.)
143
- */ stopEvent(event) {
144
- const target = event.target;
145
- // Chỉ chặn events từ control elements (handle, type-around buttons)
146
- const isControl = target.closest('[data-block-handle]') || target.closest('.widget__type-around__button') || target.closest('.type-around');
147
- if (!isControl) {
148
- // Event từ table content → cho ProseMirror xử lý
149
- return false;
150
- }
151
- // Event từ control → chặn hầu hết, NHƯNG cho phép một số events quan trọng
152
- const eventType = event.type;
153
- // Cho phép drag events để ProseMirror handle drag & drop
154
- if (eventType === 'dragstart' || eventType === 'drag' || eventType === 'dragend') {
155
- return false; // Let ProseMirror handle drag
156
- }
157
- // Cho phép focus events để không phá navigation
158
- if (eventType === 'focus' || eventType === 'blur') {
159
- return false;
160
- }
161
- // Chặn các events khác từ controls
162
- return true;
163
- }
164
- selectNode() {
165
- this.dom.classList.add('ProseMirror-selectednode');
166
- }
167
- deselectNode() {
168
- this.dom.classList.remove('ProseMirror-selectednode');
169
- }
170
- destroy() {
171
- // Cleanup all event listeners
172
- this.eventCleanups.forEach((cleanup)=>cleanup());
173
- this.eventCleanups = [];
174
- // Remove DOM
175
- this.dom.remove();
176
- }
177
- constructor(node, view, getPos){
178
- // Store event listeners for cleanup
179
- this.eventCleanups = [];
180
- this.node = node;
181
- this.view = view;
182
- this.getPos = getPos;
183
- // 1. Dùng <figure> làm wrapper, KHÔNG set contentEditable vì sẽ chặn tất cả
184
- this.dom = document.createElement('figure');
185
- this.dom.classList.add('widget');
186
- // Make the widget draggable (via the handle with data-drag-handle attribute)
187
- // this.dom.draggable = true;
188
- // 2. Tạo Handle kéo thả
189
- const handle = this.createBlockHandle(view, getPos);
190
- // 3. Tạo Nội dung (do lớp con định nghĩa)
191
- const contentElement = this.createContentElement(node);
192
- contentElement.classList.add('widget-content');
193
- // 4. Tạo các nút "Type Around"
194
- const typeAround = this.createTypeAround(view, getPos, node);
195
- // 5. Lắp ráp cấu trúc: Handle -> Nội dung -> TypeAround
196
- // this.dom.append(handle, contentElement, typeAround);
197
- this.dom.append(contentElement, typeAround);
198
- // 6. Setup drag handling for ProseMirror
199
- // this.setupDragHandling(view, getPos, node);
200
- }
201
- }
202
- export class TableNodeView extends HandleNodeView {
203
- createContentElement() {
204
- // Create table element
205
- const table = document.createElement('table');
206
- // Apply table styles (border and backgroundColor)
207
- this.updateTableStyles(table);
208
- // Create tbody - this is the contentDOM where ProseMirror renders <tr>s
209
- const tbody = document.createElement('tbody');
210
- this.contentDOM = tbody;
211
- table.appendChild(tbody);
212
- // Render colgroup
213
- this.updateColgroup(table);
214
- return table;
215
- }
216
- /**
217
- * Get table element from DOM
218
- * Query from this.dom instead of storing reference to avoid stale references
219
- */ getTableElement() {
220
- return this.dom.querySelector('table');
221
- }
222
- update(node) {
223
- // Check if node actually changed
224
- if (node.type !== this.node.type) {
225
- return false;
226
- }
227
- // Update node reference
228
- this.node = node;
229
- // Get table element
230
- const table = this.getTableElement();
231
- if (!table) {
232
- return true;
233
- }
234
- // Update table styles if border or backgroundColor changed
235
- this.updateTableStyles(table);
236
- // Update colgroup if colwidths changed
237
- this.updateColgroup(table);
238
- return true;
239
- }
240
- updateTableStyles(table) {
241
- const border = this.node.attrs['border'];
242
- const backgroundColor = this.node.attrs['backgroundColor'];
243
- // Reset styles
244
- // (Chúng ta reset để xử lý trường hợp attribute bị xóa)
245
- table.style.borderStyle = '';
246
- table.style.borderColor = '';
247
- table.style.borderWidth = '';
248
- table.style.backgroundColor = '';
249
- // Apply backgroundColor
250
- if (backgroundColor) {
251
- table.style.backgroundColor = backgroundColor;
252
- }
253
- // Apply border
254
- if (border) {
255
- if (border.style) table.style.borderStyle = border.style;
256
- if (border.color) table.style.borderColor = border.color;
257
- if (border.width) table.style.borderWidth = border.width;
258
- }
259
- }
260
- updateColgroup(table) {
261
- // Remove existing colgroup if exists
262
- const existingColgroup = table.querySelector('colgroup');
263
- if (existingColgroup) {
264
- existingColgroup.remove();
265
- }
266
- // Render new colgroup if colwidths exist
267
- const colwidths = this.node.attrs['colwidths'];
268
- if (colwidths && colwidths.length > 0) {
269
- const colgroup = document.createElement('colgroup');
270
- colwidths.forEach((width)=>{
271
- const col = document.createElement('col');
272
- if (width) {
273
- col.style.width = `${width}%`;
274
- }
275
- colgroup.appendChild(col);
276
- });
277
- // Insert before tbody
278
- const tbody = table.querySelector('tbody');
279
- if (tbody) {
280
- table.insertBefore(colgroup, tbody);
281
- } else {
282
- table.appendChild(colgroup);
283
- }
284
- }
285
- }
286
- }
287
- // Factory function for Tiptap NodeView
288
- export const createTableNodeView = (props)=>{
289
- return new TableNodeView(props.node, props.view, props.getPos);
290
- };
291
- export class PageBreakNodeView extends HandleNodeView {
292
- createContentElement() {
293
- // Create page break container
294
- const pageBreakDiv = document.createElement('div');
295
- pageBreakDiv.className = 'document-engine-page-break';
296
- pageBreakDiv.setAttribute('data-page-break', 'true');
297
- pageBreakDiv.style.cssText = 'break-before: page;';
298
- pageBreakDiv.contentEditable = 'false';
299
- // Create label
300
- const label = document.createElement('span');
301
- label.className = 'document-engine-page-break-label';
302
- label.textContent = 'Page break';
303
- label.contentEditable = 'false';
304
- pageBreakDiv.appendChild(label);
305
- return pageBreakDiv;
306
- }
307
- }
308
- // Factory function for PageBreak NodeView
309
- export const createPageBreakNodeView = (props)=>{
310
- return new PageBreakNodeView(props.node, props.view, props.getPos);
311
- };
@@ -1 +0,0 @@
1
- export * from './block-handler';