@phuong-tran-redoc/document-engine-core 0.0.17 → 0.0.19
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/package.json +4 -3
- package/src/constants/indent.constant.js +1 -11
- package/src/constants/index.js +1 -18
- package/src/constants/table.constant.js +1 -11
- package/src/extensions/clear-content.extension.js +2 -12
- package/src/extensions/indent.extension.js +4 -14
- package/src/extensions/index.js +8 -25
- package/src/extensions/ordered-list.extension.js +2 -12
- package/src/extensions/reset-format.extension.js +2 -12
- package/src/extensions/reset-on-enter.extension.js +7 -12
- package/src/extensions/restricted-editing.extension.js +8 -25
- package/src/extensions/table-resizing.extension.js +15 -22
- package/src/extensions/table-style.extension.js +29 -57
- package/src/extensions/text-case.extension.js +4 -14
- package/src/index.js +7 -24
- package/src/models/color.model.js +4 -14
- package/src/models/index.js +1 -18
- package/src/nodes/dynamic-field.node.js +9 -13
- package/src/nodes/heading.node.js +3 -13
- package/src/nodes/index.js +3 -20
- package/src/nodes/page-break.node.js +3 -13
- package/src/types/index.js +2 -19
- package/src/types/ordered-list.type.js +1 -4
- package/src/types/text-case.type.js +1 -4
- package/src/utils/color.util.js +1 -11
- package/src/utils/dom.util.js +1 -11
- package/src/utils/index.js +4 -21
- package/src/utils/table.util.js +22 -35
- package/src/utils/text.util.js +9 -22
- package/src/views/block-handler.js +9 -29
- package/src/views/index.js +1 -18
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phuong-tran-redoc/document-engine-core",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"type": "
|
|
3
|
+
"version": "0.0.19",
|
|
4
|
+
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Duc Phuong (Jack) <tdp99.business@gmail.com>",
|
|
7
7
|
"description": "Framework-agnostic core library for Document Engine",
|
|
@@ -46,5 +46,6 @@
|
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"lodash-es": "^4.17.10"
|
|
49
|
-
}
|
|
49
|
+
},
|
|
50
|
+
"module": "./src/index.js"
|
|
50
51
|
}
|
|
@@ -1,11 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "INDENT_DEFAULT", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return INDENT_DEFAULT;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const INDENT_DEFAULT = 40; // in pixels
|
|
1
|
+
export const INDENT_DEFAULT = 40; // in pixels
|
package/src/constants/index.js
CHANGED
|
@@ -1,18 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
_export_star(require("./indent.constant"), exports);
|
|
6
|
-
function _export_star(from, to) {
|
|
7
|
-
Object.keys(from).forEach(function(k) {
|
|
8
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
9
|
-
Object.defineProperty(to, k, {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
get: function() {
|
|
12
|
-
return from[k];
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
return from;
|
|
18
|
-
}
|
|
1
|
+
export * from './indent.constant';
|
|
@@ -1,11 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "MIN_NEW_COL_WIDTH", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return MIN_NEW_COL_WIDTH;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const MIN_NEW_COL_WIDTH = 5.0;
|
|
1
|
+
export const MIN_NEW_COL_WIDTH = 5.0;
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "ClearContent", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return ClearContent;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _core = require("@tiptap/core");
|
|
12
|
-
const ClearContent = _core.Extension.create({
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
export const ClearContent = Extension.create({
|
|
13
3
|
name: 'clearContent',
|
|
14
4
|
addCommands () {
|
|
15
5
|
return {
|
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "Indent", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return Indent;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _core = require("@tiptap/core");
|
|
12
|
-
const _constants = require("../constants");
|
|
13
|
-
const Indent = _core.Extension.create({
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
import { INDENT_DEFAULT } from '../constants';
|
|
3
|
+
export const Indent = Extension.create({
|
|
14
4
|
name: 'indent',
|
|
15
5
|
addOptions () {
|
|
16
6
|
return {
|
|
@@ -18,7 +8,7 @@ const Indent = _core.Extension.create({
|
|
|
18
8
|
'paragraph',
|
|
19
9
|
'heading'
|
|
20
10
|
],
|
|
21
|
-
indent:
|
|
11
|
+
indent: INDENT_DEFAULT
|
|
22
12
|
};
|
|
23
13
|
},
|
|
24
14
|
addGlobalAttributes () {
|
package/src/extensions/index.js
CHANGED
|
@@ -1,25 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
_export_star(require("./reset-on-enter.extension"), exports);
|
|
10
|
-
_export_star(require("./restricted-editing.extension"), exports);
|
|
11
|
-
_export_star(require("./table-style.extension"), exports);
|
|
12
|
-
_export_star(require("./text-case.extension"), exports);
|
|
13
|
-
function _export_star(from, to) {
|
|
14
|
-
Object.keys(from).forEach(function(k) {
|
|
15
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
16
|
-
Object.defineProperty(to, k, {
|
|
17
|
-
enumerable: true,
|
|
18
|
-
get: function() {
|
|
19
|
-
return from[k];
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
return from;
|
|
25
|
-
}
|
|
1
|
+
export * from './clear-content.extension';
|
|
2
|
+
export * from './indent.extension';
|
|
3
|
+
export * from './ordered-list.extension';
|
|
4
|
+
export * from './reset-format.extension';
|
|
5
|
+
export * from './reset-on-enter.extension';
|
|
6
|
+
export * from './restricted-editing.extension';
|
|
7
|
+
export * from './table-style.extension';
|
|
8
|
+
export * from './text-case.extension';
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "CustomOrderedList", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return CustomOrderedList;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _extensionlist = require("@tiptap/extension-list");
|
|
12
|
-
const CustomOrderedList = _extensionlist.OrderedList.extend({
|
|
1
|
+
import { OrderedList } from '@tiptap/extension-list';
|
|
2
|
+
export const CustomOrderedList = OrderedList.extend({
|
|
13
3
|
name: 'customOrderedList',
|
|
14
4
|
addOptions () {
|
|
15
5
|
return {
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "ResetFormat", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return ResetFormat;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _core = require("@tiptap/core");
|
|
12
|
-
const ResetFormat = _core.Extension.create({
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
export const ResetFormat = Extension.create({
|
|
13
3
|
name: 'resetFormat',
|
|
14
4
|
addCommands () {
|
|
15
5
|
return {
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
return ResetOnEnter;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _core = require("@tiptap/core");
|
|
12
|
-
const ResetOnEnter = _core.Extension.create({
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
/*
|
|
3
|
+
* Clear:
|
|
4
|
+
* - textStyle mark
|
|
5
|
+
* - indent attribute
|
|
6
|
+
*/ // Clears formats on Enter so the new line has default formats
|
|
7
|
+
export const ResetOnEnter = Extension.create({
|
|
13
8
|
name: 'resetOnEnter',
|
|
14
9
|
addKeyboardShortcuts () {
|
|
15
10
|
return {
|
|
@@ -1,24 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: all[name]
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
EditableRegion: function() {
|
|
13
|
-
return EditableRegion;
|
|
14
|
-
},
|
|
15
|
-
RestrictedEditing: function() {
|
|
16
|
-
return RestrictedEditing;
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
const _core = require("@tiptap/core");
|
|
20
|
-
const _state = require("@tiptap/pm/state");
|
|
21
|
-
const EditableRegion = _core.Node.create({
|
|
1
|
+
import { Extension, Node, mergeAttributes } from '@tiptap/core';
|
|
2
|
+
import { Plugin, PluginKey } from '@tiptap/pm/state';
|
|
3
|
+
// Editable inline region where typing is allowed
|
|
4
|
+
export const EditableRegion = Node.create({
|
|
22
5
|
name: 'editableRegion',
|
|
23
6
|
group: 'inline',
|
|
24
7
|
inline: true,
|
|
@@ -34,7 +17,7 @@ const EditableRegion = _core.Node.create({
|
|
|
34
17
|
renderHTML ({ HTMLAttributes }) {
|
|
35
18
|
return [
|
|
36
19
|
'span',
|
|
37
|
-
|
|
20
|
+
mergeAttributes(HTMLAttributes, {
|
|
38
21
|
'data-editable-region': 'true',
|
|
39
22
|
class: 'editable-region'
|
|
40
23
|
}),
|
|
@@ -42,7 +25,7 @@ const EditableRegion = _core.Node.create({
|
|
|
42
25
|
];
|
|
43
26
|
}
|
|
44
27
|
});
|
|
45
|
-
const RestrictedEditing =
|
|
28
|
+
export const RestrictedEditing = Extension.create({
|
|
46
29
|
name: 'restrictedEditing',
|
|
47
30
|
addOptions () {
|
|
48
31
|
return {
|
|
@@ -255,7 +238,7 @@ const RestrictedEditing = _core.Extension.create({
|
|
|
255
238
|
};
|
|
256
239
|
},
|
|
257
240
|
addProseMirrorPlugins () {
|
|
258
|
-
const key = new
|
|
241
|
+
const key = new PluginKey('restricted-editing');
|
|
259
242
|
const editor = this.editor;
|
|
260
243
|
const editableRegionNodeName = this.options.editableRegionNodeName;
|
|
261
244
|
const disabledNodeTypes = this.options.disabledNodeTypes ?? [];
|
|
@@ -292,7 +275,7 @@ const RestrictedEditing = _core.Extension.create({
|
|
|
292
275
|
return ranges;
|
|
293
276
|
};
|
|
294
277
|
return [
|
|
295
|
-
new
|
|
278
|
+
new Plugin({
|
|
296
279
|
key,
|
|
297
280
|
filterTransaction: (tr, state)=>{
|
|
298
281
|
// In standard mode, do not restrict edits
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return PercentageColumnResizing;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _core = require("@tiptap/core");
|
|
12
|
-
const _state = require("@tiptap/pm/state");
|
|
13
|
-
const _tables = require("@tiptap/pm/tables");
|
|
14
|
-
const _view = require("@tiptap/pm/view");
|
|
15
|
-
const _utils = require("../utils");
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
import { Plugin, PluginKey } from '@tiptap/pm/state';
|
|
3
|
+
import { tableNodeTypes } from '@tiptap/pm/tables';
|
|
4
|
+
import { Decoration, DecorationSet } from '@tiptap/pm/view';
|
|
5
|
+
import { getTableDOMFromView } from '../utils';
|
|
16
6
|
/**
|
|
17
7
|
* Hàm xử lý khi di chuột (kéo)
|
|
18
8
|
*/ function handleMouseMoveEvent(event, dragInfo) {
|
|
@@ -75,7 +65,10 @@ const _utils = require("../utils");
|
|
|
75
65
|
view: null
|
|
76
66
|
};
|
|
77
67
|
}
|
|
78
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Extension PercentageColumnResizing
|
|
70
|
+
* * Thay thế logic resize cột mặc định bằng logic dựa trên %.
|
|
71
|
+
*/ export const PercentageColumnResizing = Extension.create({
|
|
79
72
|
name: 'percentageColumnResizing',
|
|
80
73
|
addProseMirrorPlugins () {
|
|
81
74
|
// Một đối tượng tạm thời để lưu trữ trạng thái kéo-thả
|
|
@@ -93,8 +86,8 @@ const PercentageColumnResizing = _core.Extension.create({
|
|
|
93
86
|
};
|
|
94
87
|
const editorInstance = this.editor;
|
|
95
88
|
return [
|
|
96
|
-
new
|
|
97
|
-
key: new
|
|
89
|
+
new Plugin({
|
|
90
|
+
key: new PluginKey('percentageColumnResizing'),
|
|
98
91
|
props: {
|
|
99
92
|
/**
|
|
100
93
|
* Vẽ các resize handles vào DOM
|
|
@@ -102,7 +95,7 @@ const PercentageColumnResizing = _core.Extension.create({
|
|
|
102
95
|
if (!editorInstance?.isEditable) return;
|
|
103
96
|
const decorations = [];
|
|
104
97
|
const { doc } = state;
|
|
105
|
-
const tableTypes =
|
|
98
|
+
const tableTypes = tableNodeTypes(state.schema);
|
|
106
99
|
// Duyệt qua tất cả node trong document
|
|
107
100
|
doc.descendants((node, pos)=>{
|
|
108
101
|
if (node.type.name !== tableTypes.table.name) return;
|
|
@@ -120,10 +113,10 @@ const PercentageColumnResizing = _core.Extension.create({
|
|
|
120
113
|
handle.setAttribute('data-col-index', i.toString());
|
|
121
114
|
handle.setAttribute('data-table-pos', pos.toString());
|
|
122
115
|
// Đặt widget ngay sau thẻ <table> (pos + 1)
|
|
123
|
-
decorations.push(
|
|
116
|
+
decorations.push(Decoration.widget(pos + 1, handle));
|
|
124
117
|
}
|
|
125
118
|
});
|
|
126
|
-
return
|
|
119
|
+
return DecorationSet.create(doc, decorations);
|
|
127
120
|
},
|
|
128
121
|
/**
|
|
129
122
|
* Xử lý sự kiện mousedown trên handle
|
|
@@ -139,7 +132,7 @@ const PercentageColumnResizing = _core.Extension.create({
|
|
|
139
132
|
const colwidths = tableNode?.attrs['colwidths'];
|
|
140
133
|
if (!tableNode || !colwidths) return false;
|
|
141
134
|
// Get table DOM element (handles NodeView wrappers and nested tables)
|
|
142
|
-
const tableDOM =
|
|
135
|
+
const tableDOM = getTableDOMFromView(view, tablePos);
|
|
143
136
|
if (!tableDOM) return false;
|
|
144
137
|
// Lưu thông tin cần thiết cho việc kéo
|
|
145
138
|
dragInfo = {
|
|
@@ -1,41 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
MIN_NEW_COL_WIDTH: function() {
|
|
13
|
-
return MIN_NEW_COL_WIDTH;
|
|
14
|
-
},
|
|
15
|
-
StyledTable: function() {
|
|
16
|
-
return StyledTable;
|
|
17
|
-
},
|
|
18
|
-
StyledTableCell: function() {
|
|
19
|
-
return StyledTableCell;
|
|
20
|
-
},
|
|
21
|
-
StyledTableHeader: function() {
|
|
22
|
-
return StyledTableHeader;
|
|
23
|
-
},
|
|
24
|
-
StyledTableKit: function() {
|
|
25
|
-
return StyledTableKit;
|
|
26
|
-
},
|
|
27
|
-
TableDefaultAttributes: function() {
|
|
28
|
-
return TableDefaultAttributes;
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
const _core = require("@tiptap/core");
|
|
32
|
-
const _extensiontable = require("@tiptap/extension-table");
|
|
33
|
-
const _state = require("@tiptap/pm/state");
|
|
34
|
-
const _tables = require("@tiptap/pm/tables");
|
|
35
|
-
const _utils = require("../utils");
|
|
36
|
-
const _views = require("../views");
|
|
37
|
-
const _tableresizingextension = require("./table-resizing.extension");
|
|
38
|
-
const MIN_NEW_COL_WIDTH = 5.0;
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
import { Table, TableCell, TableHeader, TableRow } from '@tiptap/extension-table';
|
|
3
|
+
import { TextSelection } from '@tiptap/pm/state';
|
|
4
|
+
import { addColumnAfter, addColumnBefore, deleteColumn, selectionCell, TableMap } from '@tiptap/pm/tables';
|
|
5
|
+
import { getCursorCellInfo } from '../utils';
|
|
6
|
+
import { createTableNodeView } from '../views';
|
|
7
|
+
import { PercentageColumnResizing } from './table-resizing.extension';
|
|
8
|
+
export const MIN_NEW_COL_WIDTH = 5.0;
|
|
39
9
|
/**
|
|
40
10
|
* Calculate new column widths when adding a column, ensuring no column goes below MIN_NEW_COL_WIDTH
|
|
41
11
|
* @param oldColWidths - Array of current column widths
|
|
@@ -64,7 +34,7 @@ const MIN_NEW_COL_WIDTH = 5.0;
|
|
|
64
34
|
const reduction = MIN_NEW_COL_WIDTH / oldColWidths.length;
|
|
65
35
|
return oldColWidths.map((w)=>w - reduction);
|
|
66
36
|
}
|
|
67
|
-
const TableDefaultAttributes = {
|
|
37
|
+
export const TableDefaultAttributes = {
|
|
68
38
|
border: {
|
|
69
39
|
style: 'double',
|
|
70
40
|
color: '#e5e7eb',
|
|
@@ -130,7 +100,7 @@ const commonCellAttributes = {
|
|
|
130
100
|
parseHTML: (element)=>element.style.getPropertyValue('text-align') || null
|
|
131
101
|
}
|
|
132
102
|
};
|
|
133
|
-
const StyledTableCell =
|
|
103
|
+
export const StyledTableCell = TableCell.extend({
|
|
134
104
|
addAttributes () {
|
|
135
105
|
const parent = this.parent?.() ?? {};
|
|
136
106
|
return {
|
|
@@ -139,7 +109,7 @@ const StyledTableCell = _extensiontable.TableCell.extend({
|
|
|
139
109
|
};
|
|
140
110
|
}
|
|
141
111
|
});
|
|
142
|
-
const StyledTableHeader =
|
|
112
|
+
export const StyledTableHeader = TableHeader.extend({
|
|
143
113
|
addAttributes () {
|
|
144
114
|
const parent = this.parent?.() ?? {};
|
|
145
115
|
return {
|
|
@@ -148,7 +118,8 @@ const StyledTableHeader = _extensiontable.TableHeader.extend({
|
|
|
148
118
|
};
|
|
149
119
|
}
|
|
150
120
|
});
|
|
151
|
-
|
|
121
|
+
// Extend Table node to support width (%) and alignment (left|center|right)
|
|
122
|
+
export const StyledTable = Table.extend({
|
|
152
123
|
// name: 'styledTable',
|
|
153
124
|
// draggable: true,
|
|
154
125
|
addAttributes () {
|
|
@@ -261,7 +232,7 @@ const StyledTable = _extensiontable.Table.extend({
|
|
|
261
232
|
}).run();
|
|
262
233
|
// Get current table border and merge with new values
|
|
263
234
|
// Find the table node
|
|
264
|
-
const cellInfo =
|
|
235
|
+
const cellInfo = getCursorCellInfo(state);
|
|
265
236
|
if (!cellInfo) return false;
|
|
266
237
|
const currentBorder = cellInfo?.tableNode?.attrs?.['border'] || {};
|
|
267
238
|
const mergedBorder = {
|
|
@@ -323,19 +294,19 @@ const StyledTable = _extensiontable.Table.extend({
|
|
|
323
294
|
// --- BƯỚC 4: Dispatch transaction (Original code)---
|
|
324
295
|
if (dispatch) {
|
|
325
296
|
const offset = tr.selection.from + 1;
|
|
326
|
-
tr.replaceSelectionWith(tableNode).scrollIntoView().setSelection(
|
|
297
|
+
tr.replaceSelectionWith(tableNode).scrollIntoView().setSelection(TextSelection.near(tr.doc.resolve(offset)));
|
|
327
298
|
}
|
|
328
299
|
return true;
|
|
329
300
|
},
|
|
330
301
|
// 2. LỆNH ADDCOLUMNAFTER
|
|
331
302
|
addColumnAfter: ()=>({ state, dispatch })=>{
|
|
332
|
-
const $cell =
|
|
303
|
+
const $cell = selectionCell(state);
|
|
333
304
|
if (!$cell) return false;
|
|
334
305
|
const table = $cell.node(-1);
|
|
335
306
|
const tablePos = $cell.start(-1) - 1;
|
|
336
307
|
const oldColWidths = table.attrs['colwidths'];
|
|
337
308
|
// Calculate column index
|
|
338
|
-
const map =
|
|
309
|
+
const map = TableMap.get(table);
|
|
339
310
|
const cellPosInTable = $cell.pos - $cell.start(-1);
|
|
340
311
|
const colIndex = map.colCount(cellPosInTable);
|
|
341
312
|
// Calculate new column widths - ensure no column goes below MIN_NEW_COL_WIDTH
|
|
@@ -345,7 +316,7 @@ const StyledTable = _extensiontable.Table.extend({
|
|
|
345
316
|
if (dispatch) {
|
|
346
317
|
// Use the prosemirror-tables function directly to add column
|
|
347
318
|
// This modifies the transaction but doesn't dispatch it
|
|
348
|
-
|
|
319
|
+
addColumnAfter(state, (modifiedTr)=>{
|
|
349
320
|
// Add our colwidth changes to the same transaction
|
|
350
321
|
modifiedTr.setNodeMarkup(tablePos, null, {
|
|
351
322
|
...table.attrs,
|
|
@@ -359,13 +330,13 @@ const StyledTable = _extensiontable.Table.extend({
|
|
|
359
330
|
},
|
|
360
331
|
// 3. LỆNH ADDCOLUMNBEFORE
|
|
361
332
|
addColumnBefore: ()=>({ state, dispatch })=>{
|
|
362
|
-
const $cell =
|
|
333
|
+
const $cell = selectionCell(state);
|
|
363
334
|
if (!$cell) return false;
|
|
364
335
|
const table = $cell.node(-1);
|
|
365
336
|
const tablePos = $cell.start(-1) - 1;
|
|
366
337
|
const oldColWidths = table.attrs['colwidths'];
|
|
367
338
|
// Calculate column index
|
|
368
|
-
const map =
|
|
339
|
+
const map = TableMap.get(table);
|
|
369
340
|
const cellPosInTable = $cell.pos - $cell.start(-1);
|
|
370
341
|
const colIndex = map.colCount(cellPosInTable);
|
|
371
342
|
// Calculate new column widths - ensure no column goes below MIN_NEW_COL_WIDTH
|
|
@@ -373,7 +344,7 @@ const StyledTable = _extensiontable.Table.extend({
|
|
|
373
344
|
// Insert new column width at current column index (before)
|
|
374
345
|
newColWidths.splice(colIndex, 0, MIN_NEW_COL_WIDTH);
|
|
375
346
|
if (dispatch) {
|
|
376
|
-
|
|
347
|
+
addColumnBefore(state, (modifiedTr)=>{
|
|
377
348
|
modifiedTr.setNodeMarkup(tablePos, null, {
|
|
378
349
|
...table.attrs,
|
|
379
350
|
colwidths: newColWidths
|
|
@@ -385,13 +356,13 @@ const StyledTable = _extensiontable.Table.extend({
|
|
|
385
356
|
},
|
|
386
357
|
// 4. LỆNH DELETECOLUMN
|
|
387
358
|
deleteColumn: ()=>({ state, dispatch })=>{
|
|
388
|
-
const $cell =
|
|
359
|
+
const $cell = selectionCell(state);
|
|
389
360
|
if (!$cell) return false;
|
|
390
361
|
const table = $cell.node(-1);
|
|
391
362
|
const tablePos = $cell.start(-1) - 1;
|
|
392
363
|
const oldColWidths = table.attrs['colwidths'];
|
|
393
364
|
// Calculate column index
|
|
394
|
-
const map =
|
|
365
|
+
const map = TableMap.get(table);
|
|
395
366
|
const cellPosInTable = $cell.pos - $cell.start(-1);
|
|
396
367
|
const colIndex = map.colCount(cellPosInTable);
|
|
397
368
|
// Get width of column being removed
|
|
@@ -402,7 +373,7 @@ const StyledTable = _extensiontable.Table.extend({
|
|
|
402
373
|
const gainPerCol = removedWidth / remainingWidths.length;
|
|
403
374
|
const newColWidths = remainingWidths.map((w)=>w + gainPerCol);
|
|
404
375
|
if (dispatch) {
|
|
405
|
-
|
|
376
|
+
deleteColumn(state, (modifiedTr)=>{
|
|
406
377
|
modifiedTr.setNodeMarkup(tablePos, null, {
|
|
407
378
|
...table.attrs,
|
|
408
379
|
colwidths: newColWidths
|
|
@@ -468,11 +439,12 @@ const StyledTable = _extensiontable.Table.extend({
|
|
|
468
439
|
return null;
|
|
469
440
|
}
|
|
470
441
|
// Editor is editable → use custom NodeView with handle
|
|
471
|
-
return
|
|
442
|
+
return createTableNodeView(props);
|
|
472
443
|
};
|
|
473
444
|
}
|
|
474
445
|
});
|
|
475
|
-
|
|
446
|
+
// Convenience export for consumers who want the full set
|
|
447
|
+
export const StyledTableKit = Extension.create({
|
|
476
448
|
name: 'styledTableKit',
|
|
477
449
|
addExtensions () {
|
|
478
450
|
const extensions = [];
|
|
@@ -483,7 +455,7 @@ const StyledTableKit = _core.Extension.create({
|
|
|
483
455
|
}));
|
|
484
456
|
}
|
|
485
457
|
if (this.options.tableRow !== false) {
|
|
486
|
-
extensions.push(
|
|
458
|
+
extensions.push(TableRow.configure(this.options.tableRow));
|
|
487
459
|
}
|
|
488
460
|
if (this.options.tableHeader !== false) {
|
|
489
461
|
extensions.push(StyledTableHeader.configure(this.options.tableHeader));
|
|
@@ -492,7 +464,7 @@ const StyledTableKit = _core.Extension.create({
|
|
|
492
464
|
extensions.push(StyledTableCell.configure(this.options.tableCell));
|
|
493
465
|
}
|
|
494
466
|
if (this.options.table?.resizable !== false) {
|
|
495
|
-
extensions.push(
|
|
467
|
+
extensions.push(PercentageColumnResizing);
|
|
496
468
|
}
|
|
497
469
|
return extensions;
|
|
498
470
|
}
|
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "TextCase", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return TextCase;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _core = require("@tiptap/core");
|
|
12
|
-
const _state = require("@tiptap/pm/state");
|
|
13
|
-
const TextCase = _core.Extension.create({
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
import { TextSelection } from '@tiptap/pm/state';
|
|
3
|
+
export const TextCase = Extension.create({
|
|
14
4
|
name: 'textCase',
|
|
15
5
|
addOptions () {
|
|
16
6
|
return {
|
|
@@ -41,7 +31,7 @@ const TextCase = _core.Extension.create({
|
|
|
41
31
|
break;
|
|
42
32
|
}
|
|
43
33
|
tr.replaceWith($from.pos, $to.pos, state.schema.text(newText));
|
|
44
|
-
tr.setSelection(
|
|
34
|
+
tr.setSelection(TextSelection.create(tr.doc, $from.pos, $from.pos + newText.length));
|
|
45
35
|
return true;
|
|
46
36
|
}
|
|
47
37
|
};
|
package/src/index.js
CHANGED
|
@@ -1,24 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
_export_star(require("./nodes"), exports);
|
|
9
|
-
_export_star(require("./types"), exports);
|
|
10
|
-
_export_star(require("./utils"), exports);
|
|
11
|
-
_export_star(require("./views"), exports);
|
|
12
|
-
function _export_star(from, to) {
|
|
13
|
-
Object.keys(from).forEach(function(k) {
|
|
14
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
15
|
-
Object.defineProperty(to, k, {
|
|
16
|
-
enumerable: true,
|
|
17
|
-
get: function() {
|
|
18
|
-
return from[k];
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
return from;
|
|
24
|
-
}
|
|
1
|
+
export * from './constants';
|
|
2
|
+
export * from './extensions';
|
|
3
|
+
export * from './models';
|
|
4
|
+
export * from './nodes';
|
|
5
|
+
export * from './types';
|
|
6
|
+
export * from './utils';
|
|
7
|
+
export * from './views';
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "Color", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return Color;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _utils = require("../utils");
|
|
12
|
-
class Color {
|
|
1
|
+
import { normalizeColor } from '../utils';
|
|
2
|
+
export class Color {
|
|
13
3
|
static from(value, name, type) {
|
|
14
4
|
return new Color(value, name, type);
|
|
15
5
|
}
|
|
@@ -25,10 +15,10 @@ class Color {
|
|
|
25
15
|
return this.value === color.value;
|
|
26
16
|
}
|
|
27
17
|
is(color) {
|
|
28
|
-
return this.value ===
|
|
18
|
+
return this.value === normalizeColor(color);
|
|
29
19
|
}
|
|
30
20
|
constructor(value, name, type){
|
|
31
|
-
const normalizedColor =
|
|
21
|
+
const normalizedColor = normalizeColor(value);
|
|
32
22
|
if (!normalizedColor) {
|
|
33
23
|
throw new Error('Invalid or unsupported color.');
|
|
34
24
|
}
|
package/src/models/index.js
CHANGED
|
@@ -1,18 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
_export_star(require("./color.model"), exports);
|
|
6
|
-
function _export_star(from, to) {
|
|
7
|
-
Object.keys(from).forEach(function(k) {
|
|
8
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
9
|
-
Object.defineProperty(to, k, {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
get: function() {
|
|
12
|
-
return from[k];
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
return from;
|
|
18
|
-
}
|
|
1
|
+
export * from './color.model';
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _core = require("@tiptap/core");
|
|
12
|
-
const DynamicField = _core.Node.create({
|
|
1
|
+
import { Node, mergeAttributes } from '@tiptap/core';
|
|
2
|
+
/**
|
|
3
|
+
* Dynamic Field Node Extension
|
|
4
|
+
*
|
|
5
|
+
* This extension creates atomic, non-editable inline nodes that represent
|
|
6
|
+
* dynamic fields (e.g., {{customer_name}}) in document templates.
|
|
7
|
+
* These fields can be programmatically replaced with actual data later.
|
|
8
|
+
*/ export const DynamicField = Node.create({
|
|
13
9
|
name: 'dynamicField',
|
|
14
10
|
/**
|
|
15
11
|
* Node group configuration
|
|
@@ -65,7 +61,7 @@ const DynamicField = _core.Node.create({
|
|
|
65
61
|
const { fieldId, label } = node.attrs;
|
|
66
62
|
return [
|
|
67
63
|
'span',
|
|
68
|
-
|
|
64
|
+
mergeAttributes(HTMLAttributes, {
|
|
69
65
|
class: 'dynamic-field',
|
|
70
66
|
'data-field-id': fieldId,
|
|
71
67
|
'data-label': label
|
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "NotumHeading", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return NotumHeading;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _extensionheading = require("@tiptap/extension-heading");
|
|
12
|
-
require("@tiptap/extension-paragraph");
|
|
13
|
-
const NotumHeading = _extensionheading.Heading.extend({
|
|
1
|
+
import { Heading } from '@tiptap/extension-heading';
|
|
2
|
+
import '@tiptap/extension-paragraph';
|
|
3
|
+
export const NotumHeading = Heading.extend({
|
|
14
4
|
addCommands () {
|
|
15
5
|
return {
|
|
16
6
|
...this.parent?.(),
|
package/src/nodes/index.js
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
});
|
|
5
|
-
_export_star(require("./heading.node"), exports);
|
|
6
|
-
_export_star(require("./dynamic-field.node"), exports);
|
|
7
|
-
_export_star(require("./page-break.node"), exports);
|
|
8
|
-
function _export_star(from, to) {
|
|
9
|
-
Object.keys(from).forEach(function(k) {
|
|
10
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
11
|
-
Object.defineProperty(to, k, {
|
|
12
|
-
enumerable: true,
|
|
13
|
-
get: function() {
|
|
14
|
-
return from[k];
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
return from;
|
|
20
|
-
}
|
|
1
|
+
export * from './heading.node';
|
|
2
|
+
export * from './dynamic-field.node';
|
|
3
|
+
export * from './page-break.node';
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "PageBreak", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return PageBreak;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _core = require("@tiptap/core");
|
|
12
|
-
const PageBreak = _core.Node.create({
|
|
1
|
+
import { Node, mergeAttributes } from '@tiptap/core';
|
|
2
|
+
export const PageBreak = Node.create({
|
|
13
3
|
name: 'pageBreak',
|
|
14
4
|
group: 'block',
|
|
15
5
|
atom: true,
|
|
@@ -24,7 +14,7 @@ const PageBreak = _core.Node.create({
|
|
|
24
14
|
renderHTML ({ HTMLAttributes }) {
|
|
25
15
|
return [
|
|
26
16
|
'div',
|
|
27
|
-
|
|
17
|
+
mergeAttributes(HTMLAttributes, {
|
|
28
18
|
'data-page-break': 'true',
|
|
29
19
|
class: 'document-engine-page-break',
|
|
30
20
|
style: 'break-before: page;'
|
package/src/types/index.js
CHANGED
|
@@ -1,19 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
_export_star(require("./ordered-list.type"), exports);
|
|
6
|
-
_export_star(require("./text-case.type"), exports);
|
|
7
|
-
function _export_star(from, to) {
|
|
8
|
-
Object.keys(from).forEach(function(k) {
|
|
9
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
10
|
-
Object.defineProperty(to, k, {
|
|
11
|
-
enumerable: true,
|
|
12
|
-
get: function() {
|
|
13
|
-
return from[k];
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
return from;
|
|
19
|
-
}
|
|
1
|
+
export * from './ordered-list.type';
|
|
2
|
+
export * from './text-case.type';
|
package/src/utils/color.util.js
CHANGED
|
@@ -10,17 +10,7 @@
|
|
|
10
10
|
* normalizeColor('rgb(255, 0, 0)') // '#ff0000'
|
|
11
11
|
* normalizeColor('#F00') // '#f00'
|
|
12
12
|
* normalizeColor('invalid') // null
|
|
13
|
-
*/
|
|
14
|
-
Object.defineProperty(exports, "__esModule", {
|
|
15
|
-
value: true
|
|
16
|
-
});
|
|
17
|
-
Object.defineProperty(exports, "normalizeColor", {
|
|
18
|
-
enumerable: true,
|
|
19
|
-
get: function() {
|
|
20
|
-
return normalizeColor;
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
function normalizeColor(color) {
|
|
13
|
+
*/ export function normalizeColor(color) {
|
|
24
14
|
if (!color) return null;
|
|
25
15
|
const trimmed = color.trim().toLowerCase();
|
|
26
16
|
if (trimmed === 'transparent') return trimmed;
|
package/src/utils/dom.util.js
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "getClosestDomElement", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return getClosestDomElement;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
function getClosestDomElement(editor, selector) {
|
|
1
|
+
export function getClosestDomElement(editor, selector) {
|
|
12
2
|
const { view, state } = editor;
|
|
13
3
|
const { selection } = state;
|
|
14
4
|
const { from } = selection;
|
package/src/utils/index.js
CHANGED
|
@@ -1,21 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
_export_star(require("./color.util"), exports);
|
|
6
|
-
_export_star(require("./dom.util"), exports);
|
|
7
|
-
_export_star(require("./table.util"), exports);
|
|
8
|
-
_export_star(require("./text.util"), exports);
|
|
9
|
-
function _export_star(from, to) {
|
|
10
|
-
Object.keys(from).forEach(function(k) {
|
|
11
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
12
|
-
Object.defineProperty(to, k, {
|
|
13
|
-
enumerable: true,
|
|
14
|
-
get: function() {
|
|
15
|
-
return from[k];
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
return from;
|
|
21
|
-
}
|
|
1
|
+
export * from './color.util';
|
|
2
|
+
export * from './dom.util';
|
|
3
|
+
export * from './table.util';
|
|
4
|
+
export * from './text.util';
|
package/src/utils/table.util.js
CHANGED
|
@@ -1,38 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: all[name]
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
getCombinedCellAttributeValue: function() {
|
|
13
|
-
return getCombinedCellAttributeValue;
|
|
14
|
-
},
|
|
15
|
-
getCursorCellInfo: function() {
|
|
16
|
-
return getCursorCellInfo;
|
|
17
|
-
},
|
|
18
|
-
getSelectedCells: function() {
|
|
19
|
-
return getSelectedCells;
|
|
20
|
-
},
|
|
21
|
-
getTableDOMFromView: function() {
|
|
22
|
-
return getTableDOMFromView;
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
const _tables = require("@tiptap/pm/tables");
|
|
26
|
-
const _lodashes = require("lodash-es");
|
|
27
|
-
function getCursorCellInfo(state) {
|
|
1
|
+
import { CellSelection, findTable, selectionCell, TableMap } from '@tiptap/pm/tables';
|
|
2
|
+
import { isEqual } from 'lodash-es';
|
|
3
|
+
export function getCursorCellInfo(state) {
|
|
28
4
|
// 1. Dùng selectionCell để tìm vị trí bắt đầu của ô chứa con trỏ
|
|
29
|
-
const cellInfo =
|
|
5
|
+
const cellInfo = selectionCell(state);
|
|
30
6
|
// Nếu không tìm thấy (con trỏ không nằm trong ô nào), trả về null
|
|
31
7
|
if (!cellInfo) return null;
|
|
32
8
|
// cellInfo.$pos là một ResolvedPos trỏ tới vị trí bắt đầu của ô
|
|
33
|
-
const table =
|
|
9
|
+
const table = findTable(cellInfo);
|
|
34
10
|
if (!table) return null;
|
|
35
|
-
const map =
|
|
11
|
+
const map = TableMap.get(table.node);
|
|
36
12
|
// 2. Lấy vị trí tương đối của ô so với bảng
|
|
37
13
|
const cellOffset = cellInfo.pos - table.start;
|
|
38
14
|
// 3. Tìm chỉ số (index) của ô này trong "bản đồ" của bảng
|
|
@@ -52,11 +28,11 @@ function getCursorCellInfo(state) {
|
|
|
52
28
|
map
|
|
53
29
|
};
|
|
54
30
|
}
|
|
55
|
-
function getSelectedCells(state) {
|
|
31
|
+
export function getSelectedCells(state) {
|
|
56
32
|
const { selection } = state;
|
|
57
33
|
const cells = [];
|
|
58
34
|
// Trường hợp chọn nhiều ô
|
|
59
|
-
if (selection instanceof
|
|
35
|
+
if (selection instanceof CellSelection) {
|
|
60
36
|
selection.forEachCell((node, pos)=>cells.push({
|
|
61
37
|
pos,
|
|
62
38
|
node
|
|
@@ -77,18 +53,29 @@ function getSelectedCells(state) {
|
|
|
77
53
|
}
|
|
78
54
|
return cells;
|
|
79
55
|
}
|
|
80
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Quyết định giá trị thuộc tính cuối cùng từ một danh sách các cell đã chọn.
|
|
58
|
+
* @param cells Danh sách các cell được chọn.
|
|
59
|
+
* @param attributeName Tên của thuộc tính cần lấy (ví dụ: 'backgroundColor').
|
|
60
|
+
* @returns Giá trị chung, hoặc MIXED_VALUE nếu các giá trị không đồng nhất.
|
|
61
|
+
*/ export function getCombinedCellAttributeValue(cells, attributeName, fallbackValue) {
|
|
81
62
|
// Không có cell nào được chọn
|
|
82
63
|
if (cells.length === 0) return fallbackValue;
|
|
83
64
|
const firstValue = cells[0].node.attrs[attributeName];
|
|
84
65
|
for(let i = 1; i < cells.length; i++){
|
|
85
66
|
const currentValue = cells[i].node.attrs[attributeName];
|
|
86
67
|
// Dùng isEqual để so sánh object (quan trọng cho thuộc tính 'border')
|
|
87
|
-
if (!
|
|
68
|
+
if (!isEqual(currentValue, firstValue)) return undefined;
|
|
88
69
|
}
|
|
89
70
|
return firstValue;
|
|
90
71
|
}
|
|
91
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Get the actual table DOM element from a node position
|
|
74
|
+
* Handles both direct table elements and NodeView wrappers
|
|
75
|
+
* Uses :scope selector to avoid selecting nested tables
|
|
76
|
+
*
|
|
77
|
+
* @export For use in other table-related extensions
|
|
78
|
+
*/ export function getTableDOMFromView(view, tablePos) {
|
|
92
79
|
const nodeDOMEl = view.nodeDOM(tablePos);
|
|
93
80
|
if (!nodeDOMEl) return null;
|
|
94
81
|
// Case 1: nodeDOMEl is the table itself
|
package/src/utils/text.util.js
CHANGED
|
@@ -1,34 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: all[name]
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
getActiveMarkRange: function() {
|
|
13
|
-
return getActiveMarkRange;
|
|
14
|
-
},
|
|
15
|
-
getSelectedText: function() {
|
|
16
|
-
return getSelectedText;
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
const _state = require("@tiptap/pm/state");
|
|
20
|
-
function getSelectedText(editor) {
|
|
1
|
+
import { TextSelection } from '@tiptap/pm/state';
|
|
2
|
+
export function getSelectedText(editor) {
|
|
21
3
|
const { state } = editor;
|
|
22
4
|
const { selection } = state;
|
|
23
5
|
// 1. Kiểm tra nếu selection rỗng (chỉ là con trỏ) hoặc không phải là TextSelection
|
|
24
|
-
if (selection.empty || !(selection instanceof
|
|
6
|
+
if (selection.empty || !(selection instanceof TextSelection)) {
|
|
25
7
|
return null;
|
|
26
8
|
}
|
|
27
9
|
// 2. Lấy text giữa điểm đầu và điểm cuối của selection
|
|
28
10
|
const { from, to } = selection;
|
|
29
11
|
return state.doc.textBetween(from, to, ' '); // Trả về text
|
|
30
12
|
}
|
|
31
|
-
|
|
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) {
|
|
32
19
|
const { state } = editor;
|
|
33
20
|
const { $from } = state.selection;
|
|
34
21
|
const { doc } = state;
|
|
@@ -1,27 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: all[name]
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
HandleNodeView: function() {
|
|
13
|
-
return HandleNodeView;
|
|
14
|
-
},
|
|
15
|
-
TableNodeView: function() {
|
|
16
|
-
return TableNodeView;
|
|
17
|
-
},
|
|
18
|
-
createTableNodeView: function() {
|
|
19
|
-
return createTableNodeView;
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
const _model = require("@tiptap/pm/model");
|
|
23
|
-
const _state = require("@tiptap/pm/state");
|
|
24
|
-
class HandleNodeView {
|
|
1
|
+
import { DOMSerializer } from '@tiptap/pm/model';
|
|
2
|
+
import { NodeSelection, TextSelection } from '@tiptap/pm/state';
|
|
3
|
+
export class HandleNodeView {
|
|
25
4
|
setupDragHandling(view, getPos, node) {
|
|
26
5
|
const handleDragStart = (event)=>{
|
|
27
6
|
const pos = getPos();
|
|
@@ -31,7 +10,7 @@ class HandleNodeView {
|
|
|
31
10
|
event.dataTransfer.effectAllowed = 'move';
|
|
32
11
|
// ProseMirror expects this format
|
|
33
12
|
const slice = view.state.doc.slice(pos, pos + node.nodeSize);
|
|
34
|
-
const serializer =
|
|
13
|
+
const serializer = DOMSerializer.fromSchema(view.state.schema);
|
|
35
14
|
const dom = document.createElement('div');
|
|
36
15
|
dom.appendChild(serializer.serializeFragment(slice.content));
|
|
37
16
|
event.dataTransfer.setDragImage(this.dom, 0, 0);
|
|
@@ -72,7 +51,7 @@ class HandleNodeView {
|
|
|
72
51
|
event.preventDefault();
|
|
73
52
|
event.stopPropagation();
|
|
74
53
|
const pos = getPos() ?? 0;
|
|
75
|
-
const tr = view.state.tr.setSelection(
|
|
54
|
+
const tr = view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos));
|
|
76
55
|
view.dispatch(tr);
|
|
77
56
|
}
|
|
78
57
|
isDragging = false;
|
|
@@ -117,7 +96,7 @@ class HandleNodeView {
|
|
|
117
96
|
const mappedInsertPos = tr.mapping.map(insertPos, -1);
|
|
118
97
|
const selectionPos = mappedInsertPos + 1;
|
|
119
98
|
// Di chuyển con trỏ vào node mới
|
|
120
|
-
view.dispatch(tr.setSelection(
|
|
99
|
+
view.dispatch(tr.setSelection(TextSelection.create(tr.doc, selectionPos)));
|
|
121
100
|
view.focus();
|
|
122
101
|
};
|
|
123
102
|
// Button Before event handlers
|
|
@@ -220,7 +199,7 @@ class HandleNodeView {
|
|
|
220
199
|
// this.setupDragHandling(view, getPos, node);
|
|
221
200
|
}
|
|
222
201
|
}
|
|
223
|
-
class TableNodeView extends HandleNodeView {
|
|
202
|
+
export class TableNodeView extends HandleNodeView {
|
|
224
203
|
createContentElement() {
|
|
225
204
|
// Create table element
|
|
226
205
|
const table = document.createElement('table');
|
|
@@ -305,6 +284,7 @@ class TableNodeView extends HandleNodeView {
|
|
|
305
284
|
}
|
|
306
285
|
}
|
|
307
286
|
}
|
|
308
|
-
|
|
287
|
+
// Factory function for Tiptap NodeView
|
|
288
|
+
export const createTableNodeView = (props)=>{
|
|
309
289
|
return new TableNodeView(props.node, props.view, props.getPos);
|
|
310
290
|
};
|
package/src/views/index.js
CHANGED
|
@@ -1,18 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
_export_star(require("./block-handler"), exports);
|
|
6
|
-
function _export_star(from, to) {
|
|
7
|
-
Object.keys(from).forEach(function(k) {
|
|
8
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
9
|
-
Object.defineProperty(to, k, {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
get: function() {
|
|
12
|
-
return from[k];
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
return from;
|
|
18
|
-
}
|
|
1
|
+
export * from './block-handler';
|