quill-table-up 2.1.9 → 2.2.0
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 +41 -34
- package/dist/index.css +1 -1
- package/dist/index.d.ts +53 -17
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +16 -16
- package/src/__tests__/e2e/editor-page.ts +6 -0
- package/src/__tests__/e2e/table-blots.test.ts +52 -0
- package/src/__tests__/e2e/table-caption.test.ts +93 -0
- package/src/__tests__/e2e/table-hack.test.ts +43 -0
- package/src/__tests__/e2e/table-resize.test.ts +136 -5
- package/src/__tests__/e2e/table-selection.test.ts +7 -10
- package/src/__tests__/e2e/utils.ts +2 -2
- package/src/__tests__/unit/table-blots.test.ts +223 -0
- package/src/__tests__/unit/table-caption.test.ts +237 -0
- package/src/__tests__/unit/table-clipboard.test.ts +6 -6
- package/src/__tests__/unit/table-hack.test.ts +137 -63
- package/src/__tests__/unit/table-insert.test.ts +4 -4
- package/src/__tests__/unit/table-redo-undo.test.ts +252 -3
- package/src/__tests__/unit/utils.test.ts +2 -2
- package/src/__tests__/unit/utils.ts +44 -25
- package/src/__tests__/unit/vitest.d.ts +0 -1
- package/src/formats/index.ts +1 -0
- package/src/formats/overrides/scroll.ts +6 -0
- package/src/formats/table-caption-format.ts +115 -0
- package/src/formats/table-cell-format.ts +63 -33
- package/src/formats/table-cell-inner-format.ts +3 -3
- package/src/formats/table-col-format.ts +16 -12
- package/src/formats/table-colgroup-format.ts +2 -2
- package/src/formats/table-main-format.ts +0 -9
- package/src/formats/table-wrapper-format.ts +2 -2
- package/src/modules/table-clipboard.ts +21 -3
- package/src/modules/table-menu/constants.ts +18 -2
- package/src/modules/table-menu/table-menu-common.ts +0 -5
- package/src/modules/table-resize/table-resize-box.ts +74 -31
- package/src/modules/table-resize/table-resize-common.ts +8 -7
- package/src/modules/table-resize/table-resize-scale.ts +12 -5
- package/src/modules/table-scrollbar.ts +12 -7
- package/src/modules/table-selection.ts +16 -19
- package/src/style/color-picker.less +4 -2
- package/src/style/index.less +19 -0
- package/src/style/table-resize.less +16 -1
- package/src/svg/arrow-up-down.svg +11 -0
- package/src/svg/auto-full.svg +11 -1
- package/src/svg/background.svg +10 -1
- package/src/svg/border.svg +10 -1
- package/src/svg/color.svg +10 -1
- package/src/svg/copy.svg +8 -1
- package/src/svg/cut.svg +7 -1
- package/src/svg/insert-bottom.svg +6 -1
- package/src/svg/insert-left.svg +6 -1
- package/src/svg/insert-right.svg +6 -1
- package/src/svg/insert-top.svg +6 -1
- package/src/svg/merge-cell.svg +6 -1
- package/src/svg/remove-column.svg +6 -1
- package/src/svg/remove-row.svg +6 -1
- package/src/svg/remove-table.svg +6 -1
- package/src/svg/split-cell.svg +6 -1
- package/src/svg/table-head.svg +4 -0
- package/src/table-up.ts +149 -112
- package/src/utils/bem.ts +2 -2
- package/src/utils/blot-helper.ts +12 -0
- package/src/utils/color.ts +12 -12
- package/src/utils/components/button.ts +2 -2
- package/src/utils/components/color-picker.ts +2 -2
- package/src/utils/components/dialog.ts +2 -2
- package/src/utils/components/input.ts +2 -2
- package/src/utils/components/table/creator.ts +2 -2
- package/src/utils/components/table/select-box.ts +2 -2
- package/src/utils/components/tooltip.ts +2 -2
- package/src/utils/constants.ts +5 -3
- package/src/utils/position.ts +2 -2
- package/src/utils/resize-observer-helper.ts +2 -2
- package/src/utils/transformer.ts +5 -0
- package/src/utils/transition-event-helper.ts +2 -2
- package/src/utils/types.ts +5 -1
- package/src/utils/utils.ts +2 -2
|
@@ -1 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
d="M4 3h14a2 2 0 0 1 2 2v7.08a6 6 0 0 0-4.32.92H12v4h1.08c-.11.68-.11 1.35 0 2H4a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2m0 4v4h6V7zm8 0v4h6V7zm-8 6v4h6v-4zm11.94 5.5h2v-4h2v4h2l-3 3z"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|
package/src/svg/insert-left.svg
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
d="M4 3h14a2 2 0 0 1 2 2v7.08a6 6 0 0 0-4.32.92H12v4h1.08c-.11.68-.11 1.35 0 2H4a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2m0 4v4h6V7zm8 0v4h6V7zm-8 6v4h6v-4zm14.44 2v2h4v2h-4v2l-3-3z"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|
package/src/svg/insert-right.svg
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
d="M4 3h14a2 2 0 0 1 2 2v7.08a6 6 0 0 0-4.32.92H12v4h1.08c-.11.68-.11 1.35 0 2H4a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2m0 4v4h6V7zm8 0v4h6V7zm-8 6v4h6v-4zm15.44 8v-2h-4v-2h4v-2l3 3z"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|
package/src/svg/insert-top.svg
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
d="M4 3h14a2 2 0 0 1 2 2v7.08a6 6 0 0 0-4.32.92H12v4h1.08c-.11.68-.11 1.35 0 2H4a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2m0 4v4h6V7zm8 0v4h6V7zm-8 6v4h6v-4zm17.94 4.5h-2v4h-2v-4h-2l3-3z"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|
package/src/svg/merge-cell.svg
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
d="M5 10H3V4h8v2H5zm14 8h-6v2h8v-6h-2zM5 18v-4H3v6h8v-2zM21 4h-8v2h6v4h2zM8 13v2l3-3l-3-3v2H3v2zm8-2V9l-3 3l3 3v-2h5v-2z"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|
|
@@ -1 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
d="M4 2h7a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m0 8v4h7v-4zm0 6v4h7v-4zM4 4v4h7V4zm13.59 8L15 9.41L16.41 8L19 10.59L21.59 8L23 9.41L20.41 12L23 14.59L21.59 16L19 13.41L16.41 16L15 14.59z"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|
package/src/svg/remove-row.svg
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
d="M9.41 13L12 15.59L14.59 13L16 14.41L13.41 17L16 19.59L14.59 21L12 18.41L9.41 21L8 19.59L10.59 17L8 14.41zM22 9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2zM4 9h4V6H4zm6 0h4V6h-4zm6 0h4V6h-4z"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|
package/src/svg/remove-table.svg
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
d="m15.46 15.88l1.42-1.42L19 16.59l2.12-2.13l1.42 1.42L20.41 18l2.13 2.12l-1.42 1.42L19 19.41l-2.12 2.13l-1.42-1.42L17.59 18zM4 3h14a2 2 0 0 1 2 2v7.08a6 6 0 0 0-4.32.92H12v4h1.08c-.11.68-.11 1.35 0 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2m0 4v4h6V7zm8 0v4h6V7zm-8 6v4h6v-4z"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|
package/src/svg/split-cell.svg
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
d="M19 14h2v6H3v-6h2v4h14zM3 4v6h2V6h14v4h2V4zm8 7v2H8v2l-3-3l3-3v2zm5 0V9l3 3l-3 3v-2h-3v-2z"
|
|
5
|
+
/>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
2
|
+
<!-- Icon from TDesign Icons by TDesign - https://github.com/Tencent/tdesign-icons/blob/main/LICENSE -->
|
|
3
|
+
<path fill="currentColor" d="M21 10v12h-2V12H5v10H3V10zm0-8v6H3V2zm-2 2H5v2h14z" />
|
|
4
|
+
</svg>
|
package/src/table-up.ts
CHANGED
|
@@ -4,9 +4,9 @@ import type TypeKeyboard from 'quill/modules/keyboard';
|
|
|
4
4
|
import type TypeToolbar from 'quill/modules/toolbar';
|
|
5
5
|
import type { InternalModule, InternalTableSelectionModule, QuillTheme, QuillThemePicker, TableCellValue, TableConstantsData, TableTextOptions, TableUpOptions } from './utils';
|
|
6
6
|
import Quill from 'quill';
|
|
7
|
-
import { BlockOverride, ContainerFormat, ScrollOverride, TableBodyFormat, TableCellFormat, TableCellInnerFormat, TableColFormat, TableColgroupFormat, TableMainFormat, TableRowFormat, TableWrapperFormat } from './formats';
|
|
7
|
+
import { BlockOverride, ContainerFormat, ScrollOverride, TableBodyFormat, TableCaptionFormat, TableCellFormat, TableCellInnerFormat, TableColFormat, TableColgroupFormat, TableMainFormat, TableRowFormat, TableWrapperFormat } from './formats';
|
|
8
8
|
import { TableClipboard } from './modules';
|
|
9
|
-
import { blotName, createBEM, createSelectBox, debounce, findParentBlot, findParentBlots, isForbidInTable, isFunction, isNumber, isString, limitDomInViewPort, mixinClass, randomId, tableCantInsert, tableUpEvent, tableUpInternal, tableUpSize } from './utils';
|
|
9
|
+
import { blotName, createBEM, createSelectBox, cssTextToObject, debounce, findParentBlot, findParentBlots, isForbidInTable, isFunction, isNumber, isString, limitDomInViewPort, mixinClass, objectToCssText, randomId, tableCantInsert, tableUpEvent, tableUpInternal, tableUpSize } from './utils';
|
|
10
10
|
|
|
11
11
|
const Parchment = Quill.import('parchment');
|
|
12
12
|
const Delta = Quill.import('delta');
|
|
@@ -67,10 +67,10 @@ export function defaultCustomSelect(tableModule: TableUp, picker: QuillThemePick
|
|
|
67
67
|
|
|
68
68
|
function generateTableArrowHandler(up: boolean) {
|
|
69
69
|
return {
|
|
70
|
+
bindInHead: false,
|
|
70
71
|
key: up ? 'ArrowUp' : 'ArrowDown',
|
|
71
72
|
collapsed: true,
|
|
72
73
|
format: [blotName.tableCellInner],
|
|
73
|
-
bindInHead: false,
|
|
74
74
|
handler(this: { quill: Quill }, range: TypeRange, context: Context) {
|
|
75
75
|
let tableBlot: TableWrapperFormat;
|
|
76
76
|
let tableMain: TableMainFormat;
|
|
@@ -86,7 +86,25 @@ function generateTableArrowHandler(up: boolean) {
|
|
|
86
86
|
const colIds = tableMain.getColIds();
|
|
87
87
|
const direction = up ? 'prev' : 'next';
|
|
88
88
|
const childDirection = up ? 'tail' : 'head';
|
|
89
|
-
const
|
|
89
|
+
const tableCaption = tableBlot.descendants(TableCaptionFormat, 0)[0];
|
|
90
|
+
|
|
91
|
+
let aroundLine;
|
|
92
|
+
if (tableCaption) {
|
|
93
|
+
const captionSide = window.getComputedStyle(tableCaption.domNode);
|
|
94
|
+
if (direction === 'next' && captionSide.captionSide === 'bottom') {
|
|
95
|
+
aroundLine = tableCaption;
|
|
96
|
+
}
|
|
97
|
+
else if (direction === 'next') {
|
|
98
|
+
aroundLine = tableBlot.next;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
aroundLine = tableCaption;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
aroundLine = tableBlot[direction];
|
|
106
|
+
}
|
|
107
|
+
|
|
90
108
|
if (context.line[direction] || !aroundLine) {
|
|
91
109
|
return true;
|
|
92
110
|
}
|
|
@@ -114,6 +132,7 @@ function generateTableArrowHandler(up: boolean) {
|
|
|
114
132
|
export class TableUp {
|
|
115
133
|
static moduleName: string = tableUpInternal.moduleName;
|
|
116
134
|
static toolName: string = blotName.tableWrapper;
|
|
135
|
+
// TODO: add custom property `bindInHead`, but Quill doesn't export `BindingObject`
|
|
117
136
|
static keyboradHandler = {
|
|
118
137
|
'forbid remove table by backspace': {
|
|
119
138
|
bindInHead: true,
|
|
@@ -159,14 +178,25 @@ export class TableUp {
|
|
|
159
178
|
},
|
|
160
179
|
'table up': generateTableArrowHandler(true),
|
|
161
180
|
'table down': generateTableArrowHandler(false),
|
|
181
|
+
'table caption break': {
|
|
182
|
+
bindInHead: true,
|
|
183
|
+
key: 'Enter',
|
|
184
|
+
shiftKey: null,
|
|
185
|
+
format: [blotName.tableCaption],
|
|
186
|
+
handler(this: { quill: Quill }, _range: TypeRange, _context: Context) {
|
|
187
|
+
return false;
|
|
188
|
+
},
|
|
189
|
+
},
|
|
162
190
|
};
|
|
163
191
|
|
|
164
192
|
static register() {
|
|
165
193
|
TableWrapperFormat.allowedChildren = [TableMainFormat];
|
|
166
194
|
|
|
167
|
-
TableMainFormat.allowedChildren = [TableBodyFormat, TableColgroupFormat];
|
|
195
|
+
TableMainFormat.allowedChildren = [TableBodyFormat, TableColgroupFormat, TableCaptionFormat];
|
|
168
196
|
TableMainFormat.requiredContainer = TableWrapperFormat;
|
|
169
197
|
|
|
198
|
+
TableCaptionFormat.requiredContainer = TableMainFormat;
|
|
199
|
+
|
|
170
200
|
TableColgroupFormat.allowedChildren = [TableColFormat];
|
|
171
201
|
TableColgroupFormat.requiredContainer = TableMainFormat;
|
|
172
202
|
|
|
@@ -201,6 +231,7 @@ export class TableUp {
|
|
|
201
231
|
[`formats/${blotName.tableBody}`]: TableBodyFormat,
|
|
202
232
|
[`formats/${blotName.tableCol}`]: TableColFormat,
|
|
203
233
|
[`formats/${blotName.tableColgroup}`]: TableColgroupFormat,
|
|
234
|
+
[`formats/${blotName.tableCaption}`]: TableCaptionFormat,
|
|
204
235
|
[`formats/${blotName.tableMain}`]: TableMainFormat,
|
|
205
236
|
[`formats/${blotName.tableWrapper}`]: TableWrapperFormat,
|
|
206
237
|
'modules/clipboard': TableClipboard,
|
|
@@ -364,17 +395,16 @@ export class TableUp {
|
|
|
364
395
|
quillHack() {
|
|
365
396
|
const originGetSemanticHTML = this.quill.getSemanticHTML;
|
|
366
397
|
this.quill.getSemanticHTML = ((index: number = 0, length?: number) => {
|
|
367
|
-
const tableCellInnerFormat = Quill.import(`formats/${blotName.tableCellInner}`) as typeof TableCellInnerFormat;
|
|
368
|
-
const inners = this.quill.scroll.domNode.querySelectorAll(`.${tableCellInnerFormat.className}`);
|
|
369
|
-
for (const inner of Array.from(inners)) {
|
|
370
|
-
inner.setAttribute('contenteditable', String(false));
|
|
371
|
-
}
|
|
372
398
|
const html = originGetSemanticHTML.call(this.quill, index, length);
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
399
|
+
|
|
400
|
+
const tableWrapperFormat = Quill.import(`formats/${blotName.tableWrapper}`) as typeof TableWrapperFormat;
|
|
401
|
+
const parser = new DOMParser();
|
|
402
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
403
|
+
for (const node of Array.from(doc.querySelectorAll(`.${tableWrapperFormat.className} caption[contenteditable], .${tableWrapperFormat.className} td > [contenteditable]`))) {
|
|
404
|
+
node.removeAttribute('contenteditable');
|
|
376
405
|
}
|
|
377
|
-
|
|
406
|
+
|
|
407
|
+
return doc.body.innerHTML;
|
|
378
408
|
}) as typeof originGetSemanticHTML;
|
|
379
409
|
|
|
380
410
|
// make sure toolbar item can format selected cells
|
|
@@ -430,7 +460,7 @@ export class TableUp {
|
|
|
430
460
|
if (toolbar) {
|
|
431
461
|
const cleanHandler = toolbar.handlers?.clean;
|
|
432
462
|
if (cleanHandler) {
|
|
433
|
-
const cleanFormatExcludeTable = (index: number, length: number,
|
|
463
|
+
const cleanFormatExcludeTable = (index: number, length: number, changeCellStyle: false | ((styleStr: string | undefined) => string) = () => '') => {
|
|
434
464
|
// base on `removeFormat`. but not remove tableCellInner
|
|
435
465
|
const text = this.quill.getText(index, length);
|
|
436
466
|
const [line, offset] = this.quill.getLine(index + length);
|
|
@@ -458,11 +488,15 @@ export class TableUp {
|
|
|
458
488
|
|
|
459
489
|
if (attributes) {
|
|
460
490
|
const { [blotName.tableCellInner]: nullValue, ...attrs } = attributes;
|
|
461
|
-
if (
|
|
491
|
+
if (changeCellStyle) {
|
|
462
492
|
const tableCellInner = contents.slice(deltaIndex - 1, deltaIndex).ops[0];
|
|
463
493
|
if (tableCellInner && tableCellInner.attributes && tableCellInner.attributes[blotName.tableCellInner]) {
|
|
464
494
|
const tableCellInnerValue = tableCellInner.attributes[blotName.tableCellInner] as TableCellValue;
|
|
465
495
|
const { style, ...value } = tableCellInnerValue;
|
|
496
|
+
const newStyle = changeCellStyle(style);
|
|
497
|
+
if (newStyle) {
|
|
498
|
+
return { ...other, attributes: { ...attrs, [blotName.tableCellInner]: { style: newStyle, ...value } } };
|
|
499
|
+
}
|
|
466
500
|
return { ...other, attributes: { ...attrs, [blotName.tableCellInner]: value } };
|
|
467
501
|
}
|
|
468
502
|
}
|
|
@@ -486,16 +520,60 @@ export class TableUp {
|
|
|
486
520
|
}
|
|
487
521
|
// if selection range is not in table, but use the TableSelection selected cells
|
|
488
522
|
// clean all other formats in cell
|
|
489
|
-
if (tableUpModule && tableUpModule.tableSelection && tableUpModule.tableSelection.selectedTds.length > 0) {
|
|
523
|
+
if (tableUpModule && tableUpModule.tableSelection && tableUpModule.tableSelection.selectedTds.length > 0 && tableUpModule.tableSelection.table) {
|
|
524
|
+
const tableMain = Quill.find(tableUpModule.tableSelection.table) as TableMainFormat;
|
|
525
|
+
if (!tableMain) {
|
|
526
|
+
console.warn('TableMainFormat not found');
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
490
529
|
const selectedTds = tableUpModule.tableSelection.selectedTds;
|
|
491
530
|
|
|
531
|
+
// get all need clean style cells. include border-right/border-bottom effect cells
|
|
532
|
+
const editTds = new Set<TableCellFormat>();
|
|
533
|
+
const tds: { td: TableCellFormat; cleanBorder: 'bottom' | 'right' | true }[] = [];
|
|
534
|
+
for (const innerTd of selectedTds) {
|
|
535
|
+
if (innerTd.parent instanceof TableCellFormat) {
|
|
536
|
+
for (const td of innerTd.parent.getNearByCell('top')) {
|
|
537
|
+
if (editTds.has(td)) continue;
|
|
538
|
+
editTds.add(td);
|
|
539
|
+
tds.push({ td, cleanBorder: 'bottom' });
|
|
540
|
+
}
|
|
541
|
+
for (const td of innerTd.parent.getNearByCell('left')) {
|
|
542
|
+
if (editTds.has(td)) continue;
|
|
543
|
+
editTds.add(td);
|
|
544
|
+
tds.push({ td, cleanBorder: 'right' });
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
editTds.add(innerTd.parent);
|
|
548
|
+
tds.push({ td: innerTd.parent, cleanBorder: true });
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
// sort cells makesure index correct
|
|
552
|
+
const allCells = tableMain.descendants(TableCellFormat);
|
|
553
|
+
const cellIndexMap = new Map(allCells.map((cell, index) => [cell, index]));
|
|
554
|
+
tds.sort((a, b) => cellIndexMap.get(a.td)! - cellIndexMap.get(b.td)!);
|
|
555
|
+
|
|
556
|
+
// compute delta
|
|
492
557
|
let delta = new Delta();
|
|
493
558
|
let lastIndex = 0;
|
|
494
|
-
for (const
|
|
495
|
-
const index =
|
|
496
|
-
const length =
|
|
559
|
+
for (const { td, cleanBorder } of tds) {
|
|
560
|
+
const index = td.getCellInner().offset(this.quill.scroll);
|
|
561
|
+
const length = td.getCellInner().length();
|
|
497
562
|
// `line` length will include a break(\n) at the end. minus 1 to remove break
|
|
498
|
-
const diff = cleanFormatExcludeTable(
|
|
563
|
+
const diff = cleanFormatExcludeTable(
|
|
564
|
+
index,
|
|
565
|
+
length - 1,
|
|
566
|
+
(styleStr: string | undefined) => {
|
|
567
|
+
if (!styleStr || cleanBorder === true) return '';
|
|
568
|
+
// only clean border-right/border-bottom style
|
|
569
|
+
const css = cssTextToObject(styleStr);
|
|
570
|
+
const filterStyle = Object.keys(css).filter(key => !key.startsWith(`border-${cleanBorder}`)).reduce((acc: Record<string, string>, key: string) => {
|
|
571
|
+
acc[key] = css[key];
|
|
572
|
+
return acc;
|
|
573
|
+
}, {});
|
|
574
|
+
return objectToCssText(filterStyle);
|
|
575
|
+
},
|
|
576
|
+
);
|
|
499
577
|
const cellDiff = new Delta().retain(index - lastIndex).concat(diff);
|
|
500
578
|
delta = delta.concat(cellDiff);
|
|
501
579
|
lastIndex = index + length;
|
|
@@ -617,110 +695,69 @@ export class TableUp {
|
|
|
617
695
|
}
|
|
618
696
|
|
|
619
697
|
if (!tableMain) return '';
|
|
620
|
-
|
|
621
|
-
const
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const tagName = element.tagName.toLowerCase();
|
|
630
|
-
const attributes = Array.from(element.attributes)
|
|
631
|
-
.map(attr => `${attr.name}="${attr.value}"`)
|
|
632
|
-
.join(' ');
|
|
633
|
-
const startTag = `<${tagName}${attributes ? ` ${attributes}` : ''}>`;
|
|
634
|
-
const selfClosingTags = [
|
|
635
|
-
'area',
|
|
636
|
-
'base',
|
|
637
|
-
'br',
|
|
638
|
-
'col',
|
|
639
|
-
'embed',
|
|
640
|
-
'hr',
|
|
641
|
-
'img',
|
|
642
|
-
'input',
|
|
643
|
-
'link',
|
|
644
|
-
'meta',
|
|
645
|
-
'param',
|
|
646
|
-
'source',
|
|
647
|
-
'track',
|
|
648
|
-
'wbr',
|
|
649
|
-
];
|
|
650
|
-
|
|
651
|
-
return {
|
|
652
|
-
startTag,
|
|
653
|
-
endTag: selfClosingTags.includes(tagName) ? '' : `</${tagName}>`,
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
let html = '';
|
|
658
|
-
const colIds: Set<string> = new Set();
|
|
659
|
-
let tdRows = 0;
|
|
660
|
-
let lastTrId: string | null = null;
|
|
698
|
+
const tableIndex = this.quill.getIndex(tableMain);
|
|
699
|
+
const tableLength = tableMain.length();
|
|
700
|
+
const tableHTML = this.quill.getSemanticHTML(tableIndex, tableLength);
|
|
701
|
+
const parser = new DOMParser();
|
|
702
|
+
const doc = parser.parseFromString(tableHTML, 'text/html');
|
|
703
|
+
|
|
704
|
+
const cellColWidth: string[] = [];
|
|
705
|
+
const cellColIds = new Set<string>();
|
|
706
|
+
const cellIds = new Set<string>();
|
|
661
707
|
for (const td of tds) {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
td.removeAttribute('contenteditable');
|
|
708
|
+
cellColIds.add(td.colId);
|
|
709
|
+
cellIds.add(`${td.rowId}-${td.colId}`);
|
|
710
|
+
}
|
|
711
|
+
// filter col
|
|
712
|
+
for (const col of Array.from(doc.querySelectorAll('col'))) {
|
|
713
|
+
if (!cellColIds.has(col.dataset.colId!)) {
|
|
714
|
+
col.remove();
|
|
670
715
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
716
|
+
else {
|
|
717
|
+
cellColWidth.push(col.getAttribute('width')!);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
// filter td
|
|
721
|
+
let rowCount = 0;
|
|
722
|
+
let lastRowId: string | null = null;
|
|
723
|
+
for (const td of Array.from(doc.querySelectorAll('td'))) {
|
|
724
|
+
if (!cellIds.has(`${td.dataset.rowId}-${td.dataset.colId}`)) {
|
|
725
|
+
const parent = td.parentElement;
|
|
726
|
+
td.remove();
|
|
727
|
+
if (parent && parent.children.length <= 0) {
|
|
728
|
+
parent.remove();
|
|
729
|
+
}
|
|
676
730
|
}
|
|
677
731
|
else {
|
|
678
|
-
|
|
732
|
+
if (lastRowId !== td.dataset.rowId) {
|
|
733
|
+
rowCount += 1;
|
|
734
|
+
lastRowId = td.dataset.rowId!;
|
|
735
|
+
}
|
|
679
736
|
}
|
|
680
|
-
lastTrId = td.rowId;
|
|
681
|
-
colIds.add(td.colId);
|
|
682
737
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
const
|
|
686
|
-
html = `${startTag}${html}${endTag}`;
|
|
687
|
-
|
|
688
|
-
const cols = tableMain.getCols();
|
|
689
|
-
const { startTag: colgroupStartTag, endTag: colgroupEndTag } = getElementTags(colgroupBlot.domNode as HTMLElement);
|
|
690
|
-
let colStr = '';
|
|
691
|
-
let width = 0;
|
|
692
|
-
const convertCols = cols.filter(col => colIds.has(col.colId)).map(col => col.clone() as TableColFormat);
|
|
738
|
+
// calculate width
|
|
739
|
+
const cols = Array.from(doc.querySelectorAll('col'));
|
|
740
|
+
const colsValue = cols.map(col => TableColFormat.value(col));
|
|
693
741
|
if (tableMain.full) {
|
|
694
|
-
|
|
695
|
-
const totalWidth = convertCols.reduce((total, col) => total + col.width, 0);
|
|
696
|
-
const totalRemainingWidth = 100 - totalWidth;
|
|
697
|
-
const part = totalRemainingWidth / totalWidth;
|
|
742
|
+
const totalWidth = colsValue.reduce((total, col) => col.width + total, 0);
|
|
698
743
|
|
|
699
|
-
for (const col of
|
|
700
|
-
col.width
|
|
744
|
+
for (const [i, col] of colsValue.entries()) {
|
|
745
|
+
col.width = Math.round((col.width / totalWidth) * 100);
|
|
746
|
+
cols[i].setAttribute('width', `${col.width}%`);
|
|
701
747
|
}
|
|
702
748
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
const tableMainDom = tableMain.domNode.cloneNode() as HTMLElement;
|
|
711
|
-
if (!tableMain.full) {
|
|
749
|
+
else {
|
|
750
|
+
let width = 0;
|
|
751
|
+
for (const col of colsValue) {
|
|
752
|
+
width += col.width;
|
|
753
|
+
}
|
|
754
|
+
const tableMainDom = doc.querySelector('table')!;
|
|
712
755
|
tableMainDom.style.width = `${width}px`;
|
|
713
756
|
}
|
|
714
757
|
|
|
715
|
-
const { startTag: mainStartTag, endTag: mainEndTag } = getElementTags(tableMainDom);
|
|
716
|
-
html = mainStartTag + html + mainEndTag;
|
|
717
|
-
|
|
718
|
-
const { startTag: wrapperStartTag, endTag: wrapperEndTag } = getElementTags(tableMain.parent.domNode as HTMLElement);
|
|
719
|
-
html = wrapperStartTag + html + wrapperEndTag;
|
|
720
|
-
|
|
721
758
|
if (isCut) {
|
|
722
759
|
const trs = tableMain.getRows();
|
|
723
|
-
if (
|
|
760
|
+
if (rowCount === trs.length) {
|
|
724
761
|
this.removeCol(tds);
|
|
725
762
|
}
|
|
726
763
|
else {
|
|
@@ -729,7 +766,7 @@ export class TableUp {
|
|
|
729
766
|
}
|
|
730
767
|
}
|
|
731
768
|
}
|
|
732
|
-
return
|
|
769
|
+
return doc.body.innerHTML;
|
|
733
770
|
}
|
|
734
771
|
|
|
735
772
|
insertTable(rows: number, columns: number) {
|
|
@@ -962,7 +999,7 @@ export class TableUp {
|
|
|
962
999
|
colgroup.insertColByIndex(columnIndex, {
|
|
963
1000
|
tableId,
|
|
964
1001
|
colId: newColId,
|
|
965
|
-
width: tableBlot.full ?
|
|
1002
|
+
width: tableBlot.full ? 6 : 160,
|
|
966
1003
|
full: tableBlot.full,
|
|
967
1004
|
});
|
|
968
1005
|
}
|
package/src/utils/bem.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cssNamespace } from './constants';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export function createBEM(b: string, n: string = cssNamespace) {
|
|
4
4
|
const prefix = n ? `${n}-` : '';
|
|
5
5
|
return {
|
|
6
6
|
/** n-b */
|
|
@@ -20,4 +20,4 @@ export const createBEM = (b: string, n: string = cssNamespace) => {
|
|
|
20
20
|
/** is-n */
|
|
21
21
|
is: (n: string) => `is-${n}`,
|
|
22
22
|
};
|
|
23
|
-
}
|
|
23
|
+
}
|
package/src/utils/blot-helper.ts
CHANGED
|
@@ -63,6 +63,18 @@ export function findAllParentBlot(Blot: TypeParchment.Blot) {
|
|
|
63
63
|
return blots;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
export function findChildBlot<T extends TypeParchment.BlotConstructor>(parent: TypeParchment.Parent, blot: T) {
|
|
67
|
+
const descendants: InstanceType<T>[] = [];
|
|
68
|
+
const next = parent.children.iterator();
|
|
69
|
+
let cur: TypeParchment.Blot | null = null;
|
|
70
|
+
while ((cur = next())) {
|
|
71
|
+
if (cur instanceof blot) {
|
|
72
|
+
descendants.push(cur as InstanceType<T>);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return descendants;
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
function mixinProps<T = any, U = any>(target: T, source: U) {
|
|
67
79
|
for (const prop of Object.getOwnPropertyNames(source)) {
|
|
68
80
|
if (/^constructor$/.test(prop)) continue;
|
package/src/utils/color.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface RGB {
|
|
|
10
10
|
b: number;
|
|
11
11
|
a: number;
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
function normalizeValue(value: number | string, max: number | string) {
|
|
14
14
|
value = Math.min(max as number, Math.max(0, Number.parseFloat(`${value}`)));
|
|
15
15
|
|
|
16
16
|
// Handle floating point rounding errors
|
|
@@ -20,24 +20,24 @@ const normalizeValue = function (value: number | string, max: number | string) {
|
|
|
20
20
|
|
|
21
21
|
// Convert into [0, 1] range if it isn't already
|
|
22
22
|
return (value % (max as number)) / Number.parseFloat(max as string);
|
|
23
|
-
}
|
|
24
|
-
export
|
|
23
|
+
}
|
|
24
|
+
export function validateHSB(hsb: HSB): HSB {
|
|
25
25
|
return {
|
|
26
26
|
h: Math.min(360, Math.max(0, hsb.h)),
|
|
27
27
|
s: Math.min(100, Math.max(0, hsb.s)),
|
|
28
28
|
b: Math.min(100, Math.max(0, hsb.b)),
|
|
29
29
|
a: Math.min(1, Math.max(0, hsb.a)),
|
|
30
30
|
};
|
|
31
|
-
}
|
|
32
|
-
export
|
|
31
|
+
}
|
|
32
|
+
export function HEXtoRGB(hex: string): RGB {
|
|
33
33
|
hex = hex.startsWith('#') ? hex.slice(1) : hex;
|
|
34
34
|
const r = Number.parseInt(hex.slice(0, 2), 16);
|
|
35
35
|
const g = Number.parseInt(hex.slice(2, 4), 16);
|
|
36
36
|
const b = Number.parseInt(hex.slice(4, 6), 16);
|
|
37
37
|
const a = Number((Number.parseInt(hex.slice(6, 8) || 'ff', 16) / 255).toFixed(2));
|
|
38
38
|
return { r, g, b, a };
|
|
39
|
-
}
|
|
40
|
-
export
|
|
39
|
+
}
|
|
40
|
+
export function RGBtoHSB(rgb: RGB): HSB {
|
|
41
41
|
let { r, g, b, a } = rgb;
|
|
42
42
|
r = normalizeValue(r, 255);
|
|
43
43
|
g = normalizeValue(g, 255);
|
|
@@ -73,8 +73,8 @@ export const RGBtoHSB = (rgb: RGB): HSB => {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
return { h: h! * 360, s: s * 100, b: v * 100, a };
|
|
76
|
-
}
|
|
77
|
-
export
|
|
76
|
+
}
|
|
77
|
+
export function HSBtoRGB(hsb: HSB): RGB {
|
|
78
78
|
let { h, s, b, a } = hsb;
|
|
79
79
|
h = normalizeValue(h, 360) * 6;
|
|
80
80
|
s = normalizeValue(s, 100);
|
|
@@ -96,8 +96,8 @@ export const HSBtoRGB = (hsb: HSB): RGB => {
|
|
|
96
96
|
b: Math.round(v * 255),
|
|
97
97
|
a,
|
|
98
98
|
};
|
|
99
|
-
}
|
|
100
|
-
export
|
|
99
|
+
}
|
|
100
|
+
export function RGBtoHEX(rgb: RGB): string {
|
|
101
101
|
const hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16), Math.round(rgb.a * 255).toString(16)];
|
|
102
102
|
for (const key in hex) {
|
|
103
103
|
if (hex[key].length === 1) {
|
|
@@ -105,5 +105,5 @@ export const RGBtoHEX = (rgb: RGB): string => {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
return hex.join('');
|
|
108
|
-
}
|
|
108
|
+
}
|
|
109
109
|
export const HSBtoHEX = (hsb: HSB): string => RGBtoHEX(HSBtoRGB(hsb));
|
|
@@ -5,7 +5,7 @@ interface ButtonOptions {
|
|
|
5
5
|
type: 'confirm' | 'default';
|
|
6
6
|
content: HTMLElement | string;
|
|
7
7
|
}
|
|
8
|
-
export
|
|
8
|
+
export function createButton(options?: Partial<ButtonOptions>) {
|
|
9
9
|
const { type = 'default', content } = options || {};
|
|
10
10
|
const bem = createBEM('button');
|
|
11
11
|
const btn = document.createElement('button');
|
|
@@ -19,4 +19,4 @@ export const createButton = (options?: Partial<ButtonOptions>) => {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
return btn;
|
|
22
|
-
}
|
|
22
|
+
}
|
|
@@ -6,7 +6,7 @@ interface ColorPickerOptions {
|
|
|
6
6
|
color: string;
|
|
7
7
|
onChange: (color: string) => void;
|
|
8
8
|
}
|
|
9
|
-
export
|
|
9
|
+
export function createColorPicker(options: Partial<ColorPickerOptions> = {}) {
|
|
10
10
|
const contentWidth = 230;
|
|
11
11
|
const contentHeight = 150;
|
|
12
12
|
const handleSizeSec = 10;
|
|
@@ -233,4 +233,4 @@ export const createColorPicker = (options: Partial<ColorPickerOptions> = {}) =>
|
|
|
233
233
|
|
|
234
234
|
updateUI();
|
|
235
235
|
return root;
|
|
236
|
-
}
|
|
236
|
+
}
|
|
@@ -6,7 +6,7 @@ interface DialogOptions {
|
|
|
6
6
|
beforeClose?: () => void;
|
|
7
7
|
}
|
|
8
8
|
let zindex = 8000;
|
|
9
|
-
export
|
|
9
|
+
export function createDialog({ child, target = document.body, beforeClose = () => {} }: DialogOptions = {}) {
|
|
10
10
|
const bem = createBEM('dialog');
|
|
11
11
|
const appendTo = target;
|
|
12
12
|
const dialog = document.createElement('div');
|
|
@@ -38,4 +38,4 @@ export const createDialog = ({ child, target = document.body, beforeClose = () =
|
|
|
38
38
|
zindex += 1;
|
|
39
39
|
|
|
40
40
|
return { dialog, close };
|
|
41
|
-
}
|
|
41
|
+
}
|
|
@@ -7,7 +7,7 @@ interface InputOptions {
|
|
|
7
7
|
min?: number;
|
|
8
8
|
[key: string]: any;
|
|
9
9
|
}
|
|
10
|
-
export
|
|
10
|
+
export function createInputItem(label: string, options: InputOptions) {
|
|
11
11
|
const bem = createBEM('input');
|
|
12
12
|
options.type || (options.type = 'text');
|
|
13
13
|
options.value || (options.value = '');
|
|
@@ -71,4 +71,4 @@ export const createInputItem = (label: string, options: InputOptions) => {
|
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
return { item: inputItem, input, errorTip };
|
|
74
|
-
}
|
|
74
|
+
}
|