@worktile/theia 16.2.1 → 16.2.3
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/editor.module.d.ts +2 -2
- package/esm2022/plugins/paint-format/paint-format.editor.mjs +14 -11
- package/esm2022/plugins/table/components/table.component.mjs +14 -17
- package/esm2022/plugins/table/table.editor.mjs +6 -1
- package/esm2022/plugins/table/table.plugin.mjs +23 -23
- package/esm2022/plugins/table/table.store.mjs +5 -22
- package/esm2022/plugins/table/transforms/move-selection-from-cell.mjs +91 -0
- package/esm2022/plugins/table/utils/calc-anchor-position.mjs +13 -12
- package/esm2022/plugins/table/utils/create-table-position.mjs +2 -2
- package/esm2022/plugins/table/utils/get-next-cell.mjs +44 -0
- package/esm2022/plugins/table/utils/index.mjs +9 -10
- package/fesm2022/worktile-theia.mjs +716 -604
- package/fesm2022/worktile-theia.mjs.map +1 -1
- package/interfaces/editor.d.ts +1 -1
- package/package.json +1 -1
- package/plugins/paint-format/paint-format.editor.d.ts +10 -1
- package/plugins/table/table.editor.d.ts +10 -1
- package/plugins/table/table.plugin.d.ts +1 -1
- package/plugins/table/transforms/move-selection-from-cell.d.ts +7 -0
- package/plugins/table/utils/calc-anchor-position.d.ts +3 -3
- package/plugins/table/utils/get-next-cell.d.ts +4 -0
- package/plugins/table/utils/index.d.ts +8 -9
- package/queries/is-range-across-blocks.d.ts +1 -1
|
@@ -3376,49 +3376,185 @@ var TableSelectCellDirection;
|
|
|
3376
3376
|
TableSelectCellDirection[TableSelectCellDirection["left"] = 3] = "left";
|
|
3377
3377
|
})(TableSelectCellDirection || (TableSelectCellDirection = {}));
|
|
3378
3378
|
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3379
|
+
function calcSpanForRow(table, targetIndex) {
|
|
3380
|
+
const rowspans = [];
|
|
3381
|
+
table.children
|
|
3382
|
+
.filter((row, rowIndex) => rowIndex < targetIndex)
|
|
3383
|
+
.forEach((row, rowIndex) => {
|
|
3384
|
+
row.children.forEach((cell, columnIndex) => {
|
|
3385
|
+
if (cell.rowspan && cell.rowspan > 1) {
|
|
3386
|
+
const rect = {
|
|
3387
|
+
x: rowIndex,
|
|
3388
|
+
y: columnIndex,
|
|
3389
|
+
width: cell.colspan || 1,
|
|
3390
|
+
height: cell.rowspan || 1
|
|
3391
|
+
};
|
|
3392
|
+
rowspans.push(rect);
|
|
3393
|
+
}
|
|
3394
|
+
});
|
|
3395
|
+
});
|
|
3396
|
+
return table.children[0].children.map((cell, columnIndex) => {
|
|
3397
|
+
return rowspans.some(rowspan => isInside(rowspan, targetIndex, columnIndex));
|
|
3398
|
+
});
|
|
3387
3399
|
}
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3400
|
+
function calcSpanForColumn(table, targetIndex) {
|
|
3401
|
+
const rowspans = [];
|
|
3402
|
+
table.children.forEach((row, rowIndex) => {
|
|
3403
|
+
row.children
|
|
3404
|
+
.filter((cell, columnIndex) => columnIndex < targetIndex)
|
|
3405
|
+
.forEach((cell, columnIndex) => {
|
|
3406
|
+
if (cell.colspan && cell.colspan > 1) {
|
|
3407
|
+
const rect = {
|
|
3408
|
+
x: rowIndex,
|
|
3409
|
+
y: columnIndex,
|
|
3410
|
+
width: cell.colspan || 1,
|
|
3411
|
+
height: cell.rowspan || 1
|
|
3412
|
+
};
|
|
3413
|
+
rowspans.push(rect);
|
|
3414
|
+
}
|
|
3415
|
+
});
|
|
3416
|
+
});
|
|
3417
|
+
return table.children.map((cell, rowIndx) => {
|
|
3418
|
+
return rowspans.some(rowspan => isInside(rowspan, rowIndx, targetIndex));
|
|
3419
|
+
});
|
|
3420
|
+
}
|
|
3421
|
+
function getOriginCell(table, targetRowIndex, targetColumnIndex) {
|
|
3422
|
+
for (let rowIndex = 0; rowIndex <= targetRowIndex; rowIndex++) {
|
|
3423
|
+
const row = table.children[rowIndex];
|
|
3424
|
+
const originCell = row.children
|
|
3425
|
+
.filter((cell, columnIndex) => columnIndex <= targetColumnIndex)
|
|
3426
|
+
.find((cell, columnIndex) => {
|
|
3427
|
+
if (cell.rowspan > 1 || cell.colspan > 1) {
|
|
3428
|
+
const rect = {
|
|
3429
|
+
x: rowIndex,
|
|
3430
|
+
y: columnIndex,
|
|
3431
|
+
width: cell.colspan || 1,
|
|
3432
|
+
height: cell.rowspan || 1
|
|
3433
|
+
};
|
|
3434
|
+
return isInside(rect, targetRowIndex, targetColumnIndex);
|
|
3435
|
+
}
|
|
3436
|
+
});
|
|
3437
|
+
if (originCell) {
|
|
3438
|
+
return originCell;
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
function isInside(cellRect, rowIndex, columnIndex) {
|
|
3443
|
+
if (rowIndex >= cellRect.x &&
|
|
3444
|
+
rowIndex <= cellRect.x + cellRect.height - 1 &&
|
|
3445
|
+
columnIndex >= cellRect.y &&
|
|
3446
|
+
columnIndex <= cellRect.y + cellRect.width - 1) {
|
|
3447
|
+
return true;
|
|
3448
|
+
}
|
|
3449
|
+
return false;
|
|
3396
3450
|
}
|
|
3397
3451
|
|
|
3398
3452
|
/**
|
|
3399
|
-
*
|
|
3453
|
+
* 计算最小行跨距单元格
|
|
3454
|
+
* @param element TableElement
|
|
3455
|
+
* @returns
|
|
3400
3456
|
*/
|
|
3401
|
-
|
|
3402
|
-
const
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
}
|
|
3408
|
-
|
|
3457
|
+
const calculateMinRowSpanCellForRows = (element) => {
|
|
3458
|
+
const cells = element.children.map((row, index) => {
|
|
3459
|
+
const noHiddenCells = row.children.filter(cell => !cell.hidden);
|
|
3460
|
+
if (noHiddenCells.length > 0) {
|
|
3461
|
+
const minRowspan = Math.min.apply(Math, noHiddenCells.map(cell => {
|
|
3462
|
+
return cell.rowspan || 1;
|
|
3463
|
+
}));
|
|
3464
|
+
const cell = row.children.find(item => !item.hidden && (item.rowspan || 1) === minRowspan);
|
|
3465
|
+
return {
|
|
3466
|
+
cell,
|
|
3467
|
+
rowIndex: index
|
|
3468
|
+
};
|
|
3469
|
+
}
|
|
3470
|
+
else {
|
|
3471
|
+
return {
|
|
3472
|
+
rowIndex: index
|
|
3473
|
+
};
|
|
3474
|
+
}
|
|
3475
|
+
});
|
|
3476
|
+
return cells;
|
|
3477
|
+
};
|
|
3409
3478
|
/**
|
|
3410
|
-
*
|
|
3479
|
+
* 计算行控件的平均高度
|
|
3480
|
+
* @param previousCombineRowIndex
|
|
3481
|
+
* @param previousRowIndex
|
|
3482
|
+
* @param rowControls
|
|
3411
3483
|
*/
|
|
3412
|
-
|
|
3413
|
-
const
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3484
|
+
const calculateRowControlsAvgHeight = (previousCombineRowIndex, previousRowIndex, rowControls) => {
|
|
3485
|
+
const rowControl = rowControls[previousRowIndex];
|
|
3486
|
+
const count = previousCombineRowIndex - previousRowIndex;
|
|
3487
|
+
const avgHeight = Math.floor(rowControl.height / (count + 1));
|
|
3488
|
+
const firstHeight = rowControl.height - avgHeight * count;
|
|
3489
|
+
rowControl.height = firstHeight;
|
|
3490
|
+
rowControls
|
|
3491
|
+
.filter((_, index) => index > previousRowIndex && index <= previousCombineRowIndex)
|
|
3492
|
+
.forEach(rowControl => {
|
|
3493
|
+
rowControl.height = avgHeight;
|
|
3494
|
+
});
|
|
3495
|
+
};
|
|
3496
|
+
const getBelowRowHeight = (editor, cells, index, rowIndex, rowspan) => {
|
|
3497
|
+
let belowRowlHeight = 0;
|
|
3498
|
+
cells.slice(index + 1, cells.length).map(item => {
|
|
3499
|
+
if (!item.cell) {
|
|
3500
|
+
return;
|
|
3419
3501
|
}
|
|
3420
|
-
|
|
3421
|
-
|
|
3502
|
+
if (rowIndex + rowspan > item.rowIndex) {
|
|
3503
|
+
const cellDom = AngularEditor.toDOMNode(editor, item.cell);
|
|
3504
|
+
if (item.cell.rowspan > 1) {
|
|
3505
|
+
// 如果下方单元格的rowspan > 1,递归计算
|
|
3506
|
+
const height = getBelowRowHeight(editor, cells, cells.findIndex(cell => cell.rowIndex === item.rowIndex), item.rowIndex, item.cell.rowspan);
|
|
3507
|
+
belowRowlHeight += getElementHeight(cellDom) - height;
|
|
3508
|
+
}
|
|
3509
|
+
else {
|
|
3510
|
+
belowRowlHeight += getElementHeight(cellDom);
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
});
|
|
3514
|
+
return belowRowlHeight;
|
|
3515
|
+
};
|
|
3516
|
+
const calculateRowControls = (editor, element) => {
|
|
3517
|
+
const minRowSpanCellForRows = calculateMinRowSpanCellForRows(element);
|
|
3518
|
+
const rowControls = [];
|
|
3519
|
+
let previousRowIndex = 0;
|
|
3520
|
+
let previousCombineRowIndex = 0;
|
|
3521
|
+
minRowSpanCellForRows.forEach((cellInfo, index) => {
|
|
3522
|
+
if (!cellInfo.cell) {
|
|
3523
|
+
rowControls.push({
|
|
3524
|
+
height: 0,
|
|
3525
|
+
rowIndex: index
|
|
3526
|
+
});
|
|
3527
|
+
previousCombineRowIndex = index;
|
|
3528
|
+
if (index === minRowSpanCellForRows.length - 1) {
|
|
3529
|
+
calculateRowControlsAvgHeight(previousCombineRowIndex, previousRowIndex, rowControls);
|
|
3530
|
+
}
|
|
3531
|
+
return;
|
|
3532
|
+
}
|
|
3533
|
+
// calculate combine row height
|
|
3534
|
+
if (previousCombineRowIndex > previousRowIndex) {
|
|
3535
|
+
calculateRowControlsAvgHeight(previousCombineRowIndex, previousRowIndex, rowControls);
|
|
3536
|
+
previousCombineRowIndex = 0;
|
|
3537
|
+
}
|
|
3538
|
+
const cellDom = AngularEditor.toDOMNode(editor, cellInfo.cell);
|
|
3539
|
+
let height = getElementHeight(cellDom);
|
|
3540
|
+
// 当cell为合并的单元格(rowspan > 1),计算其实际高度(当前单元格的高度 - 下方合并单元格的高度)
|
|
3541
|
+
if (cellInfo.cell.rowspan > 1) {
|
|
3542
|
+
const calcHeight = height - getBelowRowHeight(editor, minRowSpanCellForRows, index, cellInfo.rowIndex, cellInfo.cell.rowspan);
|
|
3543
|
+
rowControls.push({
|
|
3544
|
+
height: calcHeight,
|
|
3545
|
+
rowIndex: cellInfo.rowIndex
|
|
3546
|
+
});
|
|
3547
|
+
}
|
|
3548
|
+
else {
|
|
3549
|
+
rowControls.push({
|
|
3550
|
+
height,
|
|
3551
|
+
rowIndex: cellInfo.rowIndex
|
|
3552
|
+
});
|
|
3553
|
+
}
|
|
3554
|
+
previousRowIndex = index;
|
|
3555
|
+
});
|
|
3556
|
+
return rowControls;
|
|
3557
|
+
};
|
|
3422
3558
|
|
|
3423
3559
|
class TablePosition {
|
|
3424
3560
|
constructor(options = {
|
|
@@ -3661,160 +3797,113 @@ class TablePosition {
|
|
|
3661
3797
|
}
|
|
3662
3798
|
|
|
3663
3799
|
const createTablePosition = (editor, path) => {
|
|
3664
|
-
path = path || editor.selection
|
|
3800
|
+
path = path || editor.selection?.anchor?.path;
|
|
3665
3801
|
if (!path) {
|
|
3666
3802
|
throw new Error('Path invalid');
|
|
3667
3803
|
}
|
|
3668
3804
|
return TablePosition.create(new TableOptions(), editor, path);
|
|
3669
3805
|
};
|
|
3670
3806
|
|
|
3807
|
+
/* cell-position 有关的函数 */
|
|
3671
3808
|
/**
|
|
3672
|
-
*
|
|
3809
|
+
* 判断是否选中了所有的单元格
|
|
3810
|
+
* @param editor 编辑器对象
|
|
3811
|
+
* @param selectedCellPositions 选中的单元格位置数组
|
|
3812
|
+
* @returns 是否选中了所有的单元格
|
|
3673
3813
|
*/
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
const startPosition = createTablePosition(editor);
|
|
3677
|
-
const endPosition = createTablePosition(editor, focus.path);
|
|
3678
|
-
// Only handle events in tables
|
|
3679
|
-
if (!startPosition.isInTable() || !endPosition.isInTable()) {
|
|
3814
|
+
const isSelectedAllCell = (editor, selectedCellPositions) => {
|
|
3815
|
+
if (!AngularEditor.isFocused(editor)) {
|
|
3680
3816
|
return false;
|
|
3681
3817
|
}
|
|
3682
|
-
|
|
3683
|
-
return
|
|
3684
|
-
}
|
|
3685
|
-
|
|
3818
|
+
const pos = createTablePosition(editor);
|
|
3819
|
+
return !!selectedCellPositions.length && pos.getHeight() * pos.getWidth() === selectedCellPositions.length;
|
|
3820
|
+
};
|
|
3686
3821
|
/**
|
|
3687
|
-
*
|
|
3822
|
+
* 获取选中的单元格位置数组
|
|
3823
|
+
* @param editor 编辑器对象
|
|
3824
|
+
* @param selectedCells 选中的单元格
|
|
3825
|
+
* @returns 选中的单元格位置
|
|
3688
3826
|
*/
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
}
|
|
3827
|
+
const getSelectedCellPositions = (editor, selectedCells) => {
|
|
3828
|
+
return selectedCells?.map((cell) => {
|
|
3829
|
+
const path = AngularEditor.findPath(editor, cell);
|
|
3830
|
+
const [row, col] = path.slice(-2);
|
|
3831
|
+
return { row, col };
|
|
3832
|
+
});
|
|
3833
|
+
};
|
|
3695
3834
|
/**
|
|
3696
|
-
*
|
|
3697
|
-
* @param
|
|
3698
|
-
* @param
|
|
3699
|
-
* @
|
|
3835
|
+
* 获取一定范围内所有的单元格位置
|
|
3836
|
+
* @param startRow 起始行
|
|
3837
|
+
* @param startCol 起始列
|
|
3838
|
+
* @param endRow 结束行
|
|
3839
|
+
* @param endCol 结束列
|
|
3840
|
+
* @returns 单元格位置
|
|
3700
3841
|
*/
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
const { row, col } = cells[0];
|
|
3707
|
-
let maxCol = col;
|
|
3708
|
-
let minCol = col;
|
|
3709
|
-
let maxRow = row;
|
|
3710
|
-
let minRow = row;
|
|
3711
|
-
const pos = createTablePosition(editor);
|
|
3712
|
-
for (let { row, col } of cells) {
|
|
3713
|
-
const { hidden, rowspan, colspan } = pos.findCellByPath({ row, col });
|
|
3714
|
-
if (hidden) {
|
|
3715
|
-
selectCellSize--;
|
|
3716
|
-
continue;
|
|
3717
|
-
}
|
|
3718
|
-
if (rowspan || colspan) {
|
|
3719
|
-
const colSpan = colspan ?? 1;
|
|
3720
|
-
const rowSpan = rowspan ?? 1;
|
|
3721
|
-
// 更新视觉选中的个数
|
|
3722
|
-
selectCellSize = selectCellSize + (rowSpan * colSpan - 1);
|
|
3723
|
-
// 纠正合并单元格下的 row 和 col 的真实值
|
|
3724
|
-
row = row + rowSpan - 1;
|
|
3725
|
-
col = col + colSpan - 1;
|
|
3842
|
+
const getCellPositionsFromRange = (startRow, startCol, endRow, endCol) => {
|
|
3843
|
+
const positions = [];
|
|
3844
|
+
for (let row = startRow; row < endRow; row++) {
|
|
3845
|
+
for (let col = startCol; col < endCol; col++) {
|
|
3846
|
+
positions.push({ row, col });
|
|
3726
3847
|
}
|
|
3727
|
-
maxCol = col > maxCol ? col : maxCol;
|
|
3728
|
-
minCol = col < minCol ? col : minCol;
|
|
3729
|
-
maxRow = row > maxRow ? row : maxRow;
|
|
3730
|
-
minRow = row < minRow ? row : minRow;
|
|
3731
3848
|
}
|
|
3732
|
-
return
|
|
3733
|
-
}
|
|
3849
|
+
return positions;
|
|
3850
|
+
};
|
|
3851
|
+
/**
|
|
3852
|
+
* 过滤重复的单元格位置
|
|
3853
|
+
* @param cells 单元格数组
|
|
3854
|
+
* @param selectedCellPositions 选中的单元格位置数组
|
|
3855
|
+
* @returns 过滤后的单元格位置数组
|
|
3856
|
+
*/
|
|
3857
|
+
const uniqueCellPosition = (cells, selectedCellPositions) => {
|
|
3858
|
+
const positions = [];
|
|
3859
|
+
const modCells = new Set();
|
|
3860
|
+
cells.concat(selectedCellPositions).forEach(cell => modCells.add(JSON.stringify(cell)));
|
|
3861
|
+
modCells.forEach((cell) => positions.push(JSON.parse(cell)));
|
|
3862
|
+
return positions;
|
|
3863
|
+
};
|
|
3734
3864
|
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
x: rowIndex,
|
|
3744
|
-
y: columnIndex,
|
|
3745
|
-
width: cell.colspan || 1,
|
|
3746
|
-
height: cell.rowspan || 1
|
|
3747
|
-
};
|
|
3748
|
-
rowspans.push(rect);
|
|
3749
|
-
}
|
|
3750
|
-
});
|
|
3751
|
-
});
|
|
3752
|
-
return table.children[0].children.map((cell, columnIndex) => {
|
|
3753
|
-
return rowspans.some(rowspan => isInside(rowspan, targetIndex, columnIndex));
|
|
3754
|
-
});
|
|
3755
|
-
}
|
|
3756
|
-
function calcSpanForColumn(table, targetIndex) {
|
|
3757
|
-
const rowspans = [];
|
|
3758
|
-
table.children.forEach((row, rowIndex) => {
|
|
3759
|
-
row.children
|
|
3760
|
-
.filter((cell, columnIndex) => columnIndex < targetIndex)
|
|
3761
|
-
.forEach((cell, columnIndex) => {
|
|
3762
|
-
if (cell.colspan && cell.colspan > 1) {
|
|
3763
|
-
const rect = {
|
|
3764
|
-
x: rowIndex,
|
|
3765
|
-
y: columnIndex,
|
|
3766
|
-
width: cell.colspan || 1,
|
|
3767
|
-
height: cell.rowspan || 1
|
|
3768
|
-
};
|
|
3769
|
-
rowspans.push(rect);
|
|
3770
|
-
}
|
|
3771
|
-
});
|
|
3772
|
-
});
|
|
3773
|
-
return table.children.map((cell, rowIndx) => {
|
|
3774
|
-
return rowspans.some(rowspan => isInside(rowspan, rowIndx, targetIndex));
|
|
3775
|
-
});
|
|
3865
|
+
/**
|
|
3866
|
+
* Create a new cell
|
|
3867
|
+
*/
|
|
3868
|
+
function createCell(opts, nodes) {
|
|
3869
|
+
return {
|
|
3870
|
+
type: opts.typeCell,
|
|
3871
|
+
children: nodes || [createEmptyContent(opts)]
|
|
3872
|
+
};
|
|
3776
3873
|
}
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
x: rowIndex,
|
|
3786
|
-
y: columnIndex,
|
|
3787
|
-
width: cell.colspan || 1,
|
|
3788
|
-
height: cell.rowspan || 1
|
|
3789
|
-
};
|
|
3790
|
-
return isInside(rect, targetRowIndex, targetColumnIndex);
|
|
3791
|
-
}
|
|
3792
|
-
});
|
|
3793
|
-
if (originCell) {
|
|
3794
|
-
return originCell;
|
|
3795
|
-
}
|
|
3796
|
-
}
|
|
3874
|
+
/**
|
|
3875
|
+
* Create a new default content block
|
|
3876
|
+
*/
|
|
3877
|
+
function createEmptyContent(opts) {
|
|
3878
|
+
return {
|
|
3879
|
+
type: opts.typeContent,
|
|
3880
|
+
children: [{ text: '' }]
|
|
3881
|
+
};
|
|
3797
3882
|
}
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3883
|
+
|
|
3884
|
+
/**
|
|
3885
|
+
* Create a new row block
|
|
3886
|
+
*/
|
|
3887
|
+
function createRow(opts, columns, getCellContent) {
|
|
3888
|
+
const cellNodes = new Array(columns).fill(1).map(i => createCell(opts, getCellContent ? getCellContent(i) : undefined));
|
|
3889
|
+
return {
|
|
3890
|
+
type: opts.typeRow,
|
|
3891
|
+
children: cellNodes
|
|
3892
|
+
};
|
|
3806
3893
|
}
|
|
3807
3894
|
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
const
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3895
|
+
/**
|
|
3896
|
+
* Create a table
|
|
3897
|
+
*/
|
|
3898
|
+
function createTable(opts, columns, rows, getCellContent) {
|
|
3899
|
+
const rowNodes = new Array(rows).fill(1).map(i => createRow(opts, columns, getCellContent ? getCellContent.bind(null, i) : undefined));
|
|
3900
|
+
return {
|
|
3901
|
+
type: opts.typeTable,
|
|
3902
|
+
children: rowNodes,
|
|
3903
|
+
options: {
|
|
3904
|
+
headerRow: true
|
|
3905
|
+
}
|
|
3906
|
+
};
|
|
3818
3907
|
}
|
|
3819
3908
|
|
|
3820
3909
|
/**
|
|
@@ -3853,267 +3942,129 @@ const getColumnsWidth = (cellRow, isColgroup = false) => {
|
|
|
3853
3942
|
Array.from(cellRow.childNodes)
|
|
3854
3943
|
.filter((n) => n.nodeType === 1)
|
|
3855
3944
|
.forEach((item) => {
|
|
3856
|
-
if (isColgroup && IS_SAFARI) {
|
|
3857
|
-
result.push(item.offsetWidth);
|
|
3858
|
-
return;
|
|
3859
|
-
}
|
|
3860
|
-
if (item.getBoundingClientRect) {
|
|
3861
|
-
result.push(item.getBoundingClientRect().width);
|
|
3862
|
-
}
|
|
3863
|
-
});
|
|
3864
|
-
return result;
|
|
3865
|
-
};
|
|
3866
|
-
/**
|
|
3867
|
-
* 计算表格列宽
|
|
3868
|
-
* @param isReadonly
|
|
3869
|
-
* @param element
|
|
3870
|
-
* @param tableWidth
|
|
3871
|
-
* @param mode
|
|
3872
|
-
* @returns number[]
|
|
3873
|
-
*/
|
|
3874
|
-
const calcColumnGroups = (isReadonly, element, tableWidth, mode) => {
|
|
3875
|
-
const columns = element?.columns;
|
|
3876
|
-
if (isReadonly) {
|
|
3877
|
-
if (columns) {
|
|
3878
|
-
const opts = new TableOptions();
|
|
3879
|
-
const isPrint = mode === TheMode.print;
|
|
3880
|
-
const newColumns = isPrint
|
|
3881
|
-
? calcPrintColumnWidth(element, opts.minWidthPx)
|
|
3882
|
-
: calcColumnWidth(element, tableWidth, opts.minWidthPx);
|
|
3883
|
-
return newColumns;
|
|
3884
|
-
}
|
|
3885
|
-
return [];
|
|
3886
|
-
}
|
|
3887
|
-
else {
|
|
3888
|
-
return columns;
|
|
3889
|
-
}
|
|
3890
|
-
};
|
|
3891
|
-
/**
|
|
3892
|
-
* 计算表格列宽
|
|
3893
|
-
* @param element
|
|
3894
|
-
* @param tableWidth
|
|
3895
|
-
* @param minWidthPx
|
|
3896
|
-
* @returns number[]
|
|
3897
|
-
*/
|
|
3898
|
-
const calcColumnWidth = (element, tableWidth, minWidthPx) => {
|
|
3899
|
-
const columns = element?.columns;
|
|
3900
|
-
const numberedColumnWidth = element?.options?.numberedColumn ? TABLE_NUMBER_COLUMN : 0;
|
|
3901
|
-
const columnsWidth = columns.reduce((a, b) => a + b.width, 0);
|
|
3902
|
-
// 总列宽大于当前表格宽度时,按照设置时的总列宽计算
|
|
3903
|
-
const columnTotalWidth = Math.max(columnsWidth, tableWidth - numberedColumnWidth);
|
|
3904
|
-
return columns.map(column => {
|
|
3905
|
-
const cellWidth = (column.width / columnsWidth) * columnTotalWidth;
|
|
3906
|
-
return { width: Math.max(cellWidth, minWidthPx) };
|
|
3907
|
-
});
|
|
3908
|
-
};
|
|
3909
|
-
/**
|
|
3910
|
-
* 打印模式下,按照原宽度比例基于当前表格宽度计算列宽
|
|
3911
|
-
* 1. 所有列的最小列宽总和大于表格宽度时,所有列返回最小宽度
|
|
3912
|
-
* @param element
|
|
3913
|
-
* @param minWidthPx
|
|
3914
|
-
* @returns number[]
|
|
3915
|
-
*/
|
|
3916
|
-
const calcPrintColumnWidth = (element, minWidthPx) => {
|
|
3917
|
-
const columns = element?.columns;
|
|
3918
|
-
const numberedColumnWidth = element?.options?.numberedColumn ? TABLE_NUMBER_COLUMN : 0;
|
|
3919
|
-
// 按照 DPI 96 的 A4 纸宽度是 794, 打印时左右 80px 的边距,所以这里取 794 - 80 * 2 = 634
|
|
3920
|
-
// 如果存在序号列,还需要在 634 基础上减去序号列的宽度,剩下的才是内容区域的宽度
|
|
3921
|
-
let columnTotalWidth = 634 - numberedColumnWidth;
|
|
3922
|
-
const columnsWidth = columns.reduce((a, b) => a + b.width, 0);
|
|
3923
|
-
// 计算所有列的 minWidth 总和
|
|
3924
|
-
const totalMinWidth = minWidthPx * columns.length;
|
|
3925
|
-
if (totalMinWidth > columnTotalWidth) {
|
|
3926
|
-
// 如果所有列的 minWidth 总和大于 columnTotalWidth,所有列返回最小宽度
|
|
3927
|
-
return columns.map(() => ({ width: minWidthPx }));
|
|
3928
|
-
}
|
|
3929
|
-
// 在剩余的宽度中按比例分配
|
|
3930
|
-
const remainingWidth = columnTotalWidth - totalMinWidth;
|
|
3931
|
-
const remainingWidthRatio = columns.map(column => column.width / columnsWidth);
|
|
3932
|
-
// 为什么减 1, 因为这个宽度是内容区域宽度,但 td 右侧还有一个边框,所以减去 1
|
|
3933
|
-
let newColumnsWidth = remainingWidthRatio.map(ratio => minWidthPx + Math.floor(ratio * remainingWidth) - 1);
|
|
3934
|
-
return columns.map((_, index) => ({
|
|
3935
|
-
width: newColumnsWidth[index]
|
|
3936
|
-
}));
|
|
3937
|
-
};
|
|
3938
|
-
|
|
3939
|
-
/* cell-position 有关的函数 */
|
|
3940
|
-
/**
|
|
3941
|
-
* 判断是否选中了所有的单元格
|
|
3942
|
-
* @param editor 编辑器对象
|
|
3943
|
-
* @param selectedCellPositions 选中的单元格位置数组
|
|
3944
|
-
* @returns 是否选中了所有的单元格
|
|
3945
|
-
*/
|
|
3946
|
-
const isSelectedAllCell = (editor, selectedCellPositions) => {
|
|
3947
|
-
if (!AngularEditor.isFocused(editor)) {
|
|
3948
|
-
return false;
|
|
3949
|
-
}
|
|
3950
|
-
const pos = createTablePosition(editor);
|
|
3951
|
-
return !!selectedCellPositions.length && pos.getHeight() * pos.getWidth() === selectedCellPositions.length;
|
|
3952
|
-
};
|
|
3953
|
-
/**
|
|
3954
|
-
* 获取选中的单元格位置数组
|
|
3955
|
-
* @param editor 编辑器对象
|
|
3956
|
-
* @param selectedCells 选中的单元格
|
|
3957
|
-
* @returns 选中的单元格位置
|
|
3958
|
-
*/
|
|
3959
|
-
const getSelectedCellPositions = (editor, selectedCells) => {
|
|
3960
|
-
return selectedCells?.map((cell) => {
|
|
3961
|
-
const path = AngularEditor.findPath(editor, cell);
|
|
3962
|
-
const [row, col] = path.slice(-2);
|
|
3963
|
-
return { row, col };
|
|
3964
|
-
});
|
|
3965
|
-
};
|
|
3966
|
-
/**
|
|
3967
|
-
* 获取一定范围内所有的单元格位置
|
|
3968
|
-
* @param startRow 起始行
|
|
3969
|
-
* @param startCol 起始列
|
|
3970
|
-
* @param endRow 结束行
|
|
3971
|
-
* @param endCol 结束列
|
|
3972
|
-
* @returns 单元格位置
|
|
3973
|
-
*/
|
|
3974
|
-
const getCellPositionsFromRange = (startRow, startCol, endRow, endCol) => {
|
|
3975
|
-
const positions = [];
|
|
3976
|
-
for (let row = startRow; row < endRow; row++) {
|
|
3977
|
-
for (let col = startCol; col < endCol; col++) {
|
|
3978
|
-
positions.push({ row, col });
|
|
3979
|
-
}
|
|
3980
|
-
}
|
|
3981
|
-
return positions;
|
|
3982
|
-
};
|
|
3983
|
-
/**
|
|
3984
|
-
* 过滤重复的单元格位置
|
|
3985
|
-
* @param cells 单元格数组
|
|
3986
|
-
* @param selectedCellPositions 选中的单元格位置数组
|
|
3987
|
-
* @returns 过滤后的单元格位置数组
|
|
3988
|
-
*/
|
|
3989
|
-
const uniqueCellPosition = (cells, selectedCellPositions) => {
|
|
3990
|
-
const positions = [];
|
|
3991
|
-
const modCells = new Set();
|
|
3992
|
-
cells.concat(selectedCellPositions).forEach(cell => modCells.add(JSON.stringify(cell)));
|
|
3993
|
-
modCells.forEach((cell) => positions.push(JSON.parse(cell)));
|
|
3994
|
-
return positions;
|
|
3995
|
-
};
|
|
3996
|
-
|
|
3997
|
-
/**
|
|
3998
|
-
* 计算最小行跨距单元格
|
|
3999
|
-
* @param element TableElement
|
|
4000
|
-
* @returns
|
|
4001
|
-
*/
|
|
4002
|
-
const calculateMinRowSpanCellForRows = (element) => {
|
|
4003
|
-
const cells = element.children.map((row, index) => {
|
|
4004
|
-
const noHiddenCells = row.children.filter(cell => !cell.hidden);
|
|
4005
|
-
if (noHiddenCells.length > 0) {
|
|
4006
|
-
const minRowspan = Math.min.apply(Math, noHiddenCells.map(cell => {
|
|
4007
|
-
return cell.rowspan || 1;
|
|
4008
|
-
}));
|
|
4009
|
-
const cell = row.children.find(item => !item.hidden && (item.rowspan || 1) === minRowspan);
|
|
4010
|
-
return {
|
|
4011
|
-
cell,
|
|
4012
|
-
rowIndex: index
|
|
4013
|
-
};
|
|
4014
|
-
}
|
|
4015
|
-
else {
|
|
4016
|
-
return {
|
|
4017
|
-
rowIndex: index
|
|
4018
|
-
};
|
|
4019
|
-
}
|
|
4020
|
-
});
|
|
4021
|
-
return cells;
|
|
4022
|
-
};
|
|
4023
|
-
/**
|
|
4024
|
-
* 计算行控件的平均高度
|
|
4025
|
-
* @param previousCombineRowIndex
|
|
4026
|
-
* @param previousRowIndex
|
|
4027
|
-
* @param rowControls
|
|
4028
|
-
*/
|
|
4029
|
-
const calculateRowControlsAvgHeight = (previousCombineRowIndex, previousRowIndex, rowControls) => {
|
|
4030
|
-
const rowControl = rowControls[previousRowIndex];
|
|
4031
|
-
const count = previousCombineRowIndex - previousRowIndex;
|
|
4032
|
-
const avgHeight = Math.floor(rowControl.height / (count + 1));
|
|
4033
|
-
const firstHeight = rowControl.height - avgHeight * count;
|
|
4034
|
-
rowControl.height = firstHeight;
|
|
4035
|
-
rowControls
|
|
4036
|
-
.filter((_, index) => index > previousRowIndex && index <= previousCombineRowIndex)
|
|
4037
|
-
.forEach(rowControl => {
|
|
4038
|
-
rowControl.height = avgHeight;
|
|
4039
|
-
});
|
|
4040
|
-
};
|
|
4041
|
-
const getBelowRowHeight = (editor, cells, index, rowIndex, rowspan) => {
|
|
4042
|
-
let belowRowlHeight = 0;
|
|
4043
|
-
cells.slice(index + 1, cells.length).map(item => {
|
|
4044
|
-
if (!item.cell) {
|
|
4045
|
-
return;
|
|
4046
|
-
}
|
|
4047
|
-
if (rowIndex + rowspan > item.rowIndex) {
|
|
4048
|
-
const cellDom = AngularEditor.toDOMNode(editor, item.cell);
|
|
4049
|
-
if (item.cell.rowspan > 1) {
|
|
4050
|
-
// 如果下方单元格的rowspan > 1,递归计算
|
|
4051
|
-
const height = getBelowRowHeight(editor, cells, cells.findIndex(cell => cell.rowIndex === item.rowIndex), item.rowIndex, item.cell.rowspan);
|
|
4052
|
-
belowRowlHeight += getElementHeight(cellDom) - height;
|
|
4053
|
-
}
|
|
4054
|
-
else {
|
|
4055
|
-
belowRowlHeight += getElementHeight(cellDom);
|
|
4056
|
-
}
|
|
4057
|
-
}
|
|
4058
|
-
});
|
|
4059
|
-
return belowRowlHeight;
|
|
4060
|
-
};
|
|
4061
|
-
const calculateRowControls = (editor, element) => {
|
|
4062
|
-
const minRowSpanCellForRows = calculateMinRowSpanCellForRows(element);
|
|
4063
|
-
const rowControls = [];
|
|
4064
|
-
let previousRowIndex = 0;
|
|
4065
|
-
let previousCombineRowIndex = 0;
|
|
4066
|
-
minRowSpanCellForRows.forEach((cellInfo, index) => {
|
|
4067
|
-
if (!cellInfo.cell) {
|
|
4068
|
-
rowControls.push({
|
|
4069
|
-
height: 0,
|
|
4070
|
-
rowIndex: index
|
|
4071
|
-
});
|
|
4072
|
-
previousCombineRowIndex = index;
|
|
4073
|
-
if (index === minRowSpanCellForRows.length - 1) {
|
|
4074
|
-
calculateRowControlsAvgHeight(previousCombineRowIndex, previousRowIndex, rowControls);
|
|
4075
|
-
}
|
|
4076
|
-
return;
|
|
4077
|
-
}
|
|
4078
|
-
// calculate combine row height
|
|
4079
|
-
if (previousCombineRowIndex > previousRowIndex) {
|
|
4080
|
-
calculateRowControlsAvgHeight(previousCombineRowIndex, previousRowIndex, rowControls);
|
|
4081
|
-
previousCombineRowIndex = 0;
|
|
3945
|
+
if (isColgroup && IS_SAFARI) {
|
|
3946
|
+
result.push(item.offsetWidth);
|
|
3947
|
+
return;
|
|
4082
3948
|
}
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
// 当cell为合并的单元格(rowspan > 1),计算其实际高度(当前单元格的高度 - 下方合并单元格的高度)
|
|
4086
|
-
if (cellInfo.cell.rowspan > 1) {
|
|
4087
|
-
const calcHeight = height - getBelowRowHeight(editor, minRowSpanCellForRows, index, cellInfo.rowIndex, cellInfo.cell.rowspan);
|
|
4088
|
-
rowControls.push({
|
|
4089
|
-
height: calcHeight,
|
|
4090
|
-
rowIndex: cellInfo.rowIndex
|
|
4091
|
-
});
|
|
3949
|
+
if (item.getBoundingClientRect) {
|
|
3950
|
+
result.push(item.getBoundingClientRect().width);
|
|
4092
3951
|
}
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
3952
|
+
});
|
|
3953
|
+
return result;
|
|
3954
|
+
};
|
|
3955
|
+
/**
|
|
3956
|
+
* 计算表格列宽
|
|
3957
|
+
* @param isReadonly
|
|
3958
|
+
* @param element
|
|
3959
|
+
* @param tableWidth
|
|
3960
|
+
* @param mode
|
|
3961
|
+
* @returns number[]
|
|
3962
|
+
*/
|
|
3963
|
+
const calcColumnGroups = (isReadonly, element, tableWidth, mode) => {
|
|
3964
|
+
const columns = element?.columns;
|
|
3965
|
+
if (isReadonly) {
|
|
3966
|
+
if (columns) {
|
|
3967
|
+
const opts = new TableOptions();
|
|
3968
|
+
const isPrint = mode === TheMode.print;
|
|
3969
|
+
const newColumns = isPrint
|
|
3970
|
+
? calcPrintColumnWidth(element, opts.minWidthPx)
|
|
3971
|
+
: calcColumnWidth(element, tableWidth, opts.minWidthPx);
|
|
3972
|
+
return newColumns;
|
|
4098
3973
|
}
|
|
4099
|
-
|
|
3974
|
+
return [];
|
|
3975
|
+
}
|
|
3976
|
+
else {
|
|
3977
|
+
return columns;
|
|
3978
|
+
}
|
|
3979
|
+
};
|
|
3980
|
+
/**
|
|
3981
|
+
* 计算表格列宽
|
|
3982
|
+
* @param element
|
|
3983
|
+
* @param tableWidth
|
|
3984
|
+
* @param minWidthPx
|
|
3985
|
+
* @returns number[]
|
|
3986
|
+
*/
|
|
3987
|
+
const calcColumnWidth = (element, tableWidth, minWidthPx) => {
|
|
3988
|
+
const columns = element?.columns;
|
|
3989
|
+
const numberedColumnWidth = element?.options?.numberedColumn ? TABLE_NUMBER_COLUMN : 0;
|
|
3990
|
+
const columnsWidth = columns.reduce((a, b) => a + b.width, 0);
|
|
3991
|
+
// 总列宽大于当前表格宽度时,按照设置时的总列宽计算
|
|
3992
|
+
const columnTotalWidth = Math.max(columnsWidth, tableWidth - numberedColumnWidth);
|
|
3993
|
+
return columns.map(column => {
|
|
3994
|
+
const cellWidth = (column.width / columnsWidth) * columnTotalWidth;
|
|
3995
|
+
return { width: Math.max(cellWidth, minWidthPx) };
|
|
4100
3996
|
});
|
|
4101
|
-
return rowControls;
|
|
4102
3997
|
};
|
|
4103
|
-
|
|
4104
3998
|
/**
|
|
4105
|
-
*
|
|
4106
|
-
*
|
|
3999
|
+
* 打印模式下,按照原宽度比例基于当前表格宽度计算列宽
|
|
4000
|
+
* 1. 所有列的最小列宽总和大于表格宽度时,所有列返回最小宽度
|
|
4001
|
+
* @param element
|
|
4002
|
+
* @param minWidthPx
|
|
4003
|
+
* @returns number[]
|
|
4107
4004
|
*/
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4005
|
+
const calcPrintColumnWidth = (element, minWidthPx) => {
|
|
4006
|
+
const columns = element?.columns;
|
|
4007
|
+
const numberedColumnWidth = element?.options?.numberedColumn ? TABLE_NUMBER_COLUMN : 0;
|
|
4008
|
+
// 按照 DPI 96 的 A4 纸宽度是 794, 打印时左右 80px 的边距,所以这里取 794 - 80 * 2 = 634
|
|
4009
|
+
// 如果存在序号列,还需要在 634 基础上减去序号列的宽度,剩下的才是内容区域的宽度
|
|
4010
|
+
let columnTotalWidth = 634 - numberedColumnWidth;
|
|
4011
|
+
const columnsWidth = columns.reduce((a, b) => a + b.width, 0);
|
|
4012
|
+
// 计算所有列的 minWidth 总和
|
|
4013
|
+
const totalMinWidth = minWidthPx * columns.length;
|
|
4014
|
+
if (totalMinWidth > columnTotalWidth) {
|
|
4015
|
+
// 如果所有列的 minWidth 总和大于 columnTotalWidth,所有列返回最小宽度
|
|
4016
|
+
return columns.map(() => ({ width: minWidthPx }));
|
|
4111
4017
|
}
|
|
4112
|
-
//
|
|
4113
|
-
|
|
4114
|
-
|
|
4018
|
+
// 在剩余的宽度中按比例分配
|
|
4019
|
+
const remainingWidth = columnTotalWidth - totalMinWidth;
|
|
4020
|
+
const remainingWidthRatio = columns.map(column => column.width / columnsWidth);
|
|
4021
|
+
// 为什么减 1, 因为这个宽度是内容区域宽度,但 td 右侧还有一个边框,所以减去 1
|
|
4022
|
+
let newColumnsWidth = remainingWidthRatio.map(ratio => minWidthPx + Math.floor(ratio * remainingWidth) - 1);
|
|
4023
|
+
return columns.map((_, index) => ({
|
|
4024
|
+
width: newColumnsWidth[index]
|
|
4025
|
+
}));
|
|
4026
|
+
};
|
|
4027
|
+
|
|
4028
|
+
const getNextCell = (e, direction, cellPath) => {
|
|
4029
|
+
const [row, col] = cellPath;
|
|
4030
|
+
const pos = createTablePosition(e);
|
|
4031
|
+
let cell;
|
|
4032
|
+
switch (direction) {
|
|
4033
|
+
case TableSelectCellDirection.up:
|
|
4034
|
+
const aboveRow = pos.table.children[pos.getRowIndex() - 1];
|
|
4035
|
+
cell = aboveRow && aboveRow.children[pos.getColumnIndex()];
|
|
4036
|
+
break;
|
|
4037
|
+
case TableSelectCellDirection.down:
|
|
4038
|
+
let belowRowIndex = pos.getRowIndex() + 1;
|
|
4039
|
+
if (pos.cell.rowspan > 1) {
|
|
4040
|
+
belowRowIndex = pos.getRowIndex() + pos.cell.rowspan;
|
|
4041
|
+
}
|
|
4042
|
+
const belowRow = pos.table.children[belowRowIndex];
|
|
4043
|
+
cell = belowRow && belowRow.children[pos.getColumnIndex()];
|
|
4044
|
+
break;
|
|
4045
|
+
case TableSelectCellDirection.right:
|
|
4046
|
+
const afterPoint = Editor.after(e, e.selection);
|
|
4047
|
+
const afterPosition = afterPoint && createTablePosition(e, afterPoint.path);
|
|
4048
|
+
if (afterPosition && afterPosition.cell?.hidden) {
|
|
4049
|
+
cell = afterPosition.findNext();
|
|
4050
|
+
}
|
|
4051
|
+
else {
|
|
4052
|
+
cell = pos.findCellByPath({ row: row, col: col + 1 });
|
|
4053
|
+
}
|
|
4054
|
+
break;
|
|
4055
|
+
case TableSelectCellDirection.left:
|
|
4056
|
+
const beforePoint = Editor.before(e, e.selection);
|
|
4057
|
+
const beforePosition = beforePoint && createTablePosition(e, beforePoint.path);
|
|
4058
|
+
if (beforePosition && beforePosition.cell?.hidden) {
|
|
4059
|
+
cell = beforePosition.findPrevious();
|
|
4060
|
+
}
|
|
4061
|
+
else {
|
|
4062
|
+
cell = pos.findCellByPath({ row: row, col: col - 1 });
|
|
4063
|
+
}
|
|
4064
|
+
break;
|
|
4115
4065
|
}
|
|
4116
|
-
|
|
4066
|
+
return cell;
|
|
4067
|
+
};
|
|
4117
4068
|
|
|
4118
4069
|
function getSelectCellNode(editor, selectedCells) {
|
|
4119
4070
|
const pos = createTablePosition(editor);
|
|
@@ -4135,6 +4086,96 @@ function focusCell(editor, path) {
|
|
|
4135
4086
|
Transforms.select(editor, at);
|
|
4136
4087
|
}
|
|
4137
4088
|
|
|
4089
|
+
/**
|
|
4090
|
+
* compatible with old data
|
|
4091
|
+
* @returns
|
|
4092
|
+
*/
|
|
4093
|
+
function isHeaderRow(element) {
|
|
4094
|
+
if (element?.options?.headerRow) {
|
|
4095
|
+
return true;
|
|
4096
|
+
}
|
|
4097
|
+
// compat old data
|
|
4098
|
+
if (element?.children[0].header) {
|
|
4099
|
+
return true;
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
|
|
4103
|
+
/**
|
|
4104
|
+
* True if the given range is inside one table
|
|
4105
|
+
*/
|
|
4106
|
+
function isRangeInTable(opts, editor, range) {
|
|
4107
|
+
const { focus } = range;
|
|
4108
|
+
const startPosition = createTablePosition(editor);
|
|
4109
|
+
const endPosition = createTablePosition(editor, focus.path);
|
|
4110
|
+
// Only handle events in tables
|
|
4111
|
+
if (!startPosition.isInTable() || !endPosition.isInTable()) {
|
|
4112
|
+
return false;
|
|
4113
|
+
}
|
|
4114
|
+
// Inside the same table
|
|
4115
|
+
return startPosition.table === endPosition.table;
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
/**
|
|
4119
|
+
* Is the selection in a table
|
|
4120
|
+
*/
|
|
4121
|
+
function isSelectionInTable(opts, editor) {
|
|
4122
|
+
if (!editor.selection) {
|
|
4123
|
+
return false;
|
|
4124
|
+
}
|
|
4125
|
+
return isRangeInTable(opts, editor, editor.selection);
|
|
4126
|
+
}
|
|
4127
|
+
/**
|
|
4128
|
+
* 传入的单元格能否组成一个矩形
|
|
4129
|
+
* @param editor
|
|
4130
|
+
* @param cells
|
|
4131
|
+
* @returns boolean
|
|
4132
|
+
*/
|
|
4133
|
+
function isRectangularInTableCells(editor, cells) {
|
|
4134
|
+
let selectCellSize = cells.length;
|
|
4135
|
+
if (selectCellSize === 0) {
|
|
4136
|
+
return false;
|
|
4137
|
+
}
|
|
4138
|
+
const { row, col } = cells[0];
|
|
4139
|
+
let maxCol = col;
|
|
4140
|
+
let minCol = col;
|
|
4141
|
+
let maxRow = row;
|
|
4142
|
+
let minRow = row;
|
|
4143
|
+
const pos = createTablePosition(editor);
|
|
4144
|
+
for (let { row, col } of cells) {
|
|
4145
|
+
const { hidden, rowspan, colspan } = pos.findCellByPath({ row, col });
|
|
4146
|
+
if (hidden) {
|
|
4147
|
+
selectCellSize--;
|
|
4148
|
+
continue;
|
|
4149
|
+
}
|
|
4150
|
+
if (rowspan || colspan) {
|
|
4151
|
+
const colSpan = colspan ?? 1;
|
|
4152
|
+
const rowSpan = rowspan ?? 1;
|
|
4153
|
+
// 更新视觉选中的个数
|
|
4154
|
+
selectCellSize = selectCellSize + (rowSpan * colSpan - 1);
|
|
4155
|
+
// 纠正合并单元格下的 row 和 col 的真实值
|
|
4156
|
+
row = row + rowSpan - 1;
|
|
4157
|
+
col = col + colSpan - 1;
|
|
4158
|
+
}
|
|
4159
|
+
maxCol = col > maxCol ? col : maxCol;
|
|
4160
|
+
minCol = col < minCol ? col : minCol;
|
|
4161
|
+
maxRow = row > maxRow ? row : maxRow;
|
|
4162
|
+
minRow = row < minRow ? row : minRow;
|
|
4163
|
+
}
|
|
4164
|
+
return (maxRow + 1 - minRow) * (maxCol + 1 - minCol) === selectCellSize;
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
function isVirtualKey(e) {
|
|
4168
|
+
const isMod = e.ctrlKey || e.metaKey;
|
|
4169
|
+
const isAlt = isKeyHotkey('alt', e);
|
|
4170
|
+
const isShift = isKeyHotkey('shift', e);
|
|
4171
|
+
const isCapsLock = e.key.includes('CapsLock');
|
|
4172
|
+
const isTab = e.key.includes('Tab');
|
|
4173
|
+
const isEsc = e.key.includes('Escape');
|
|
4174
|
+
const isF = e.key.startsWith('F');
|
|
4175
|
+
const isArrow = e.key.includes('Arrow') ? true : false;
|
|
4176
|
+
return isCapsLock || isMod || isAlt || isArrow || isShift || isTab || isEsc || isF;
|
|
4177
|
+
}
|
|
4178
|
+
|
|
4138
4179
|
const setMarks = (editor, marks, at) => {
|
|
4139
4180
|
Transforms.setNodes(editor, marks, {
|
|
4140
4181
|
at,
|
|
@@ -6526,6 +6567,11 @@ const TableEditor = {
|
|
|
6526
6567
|
setMarks(editor, unsetMarks, cellRange);
|
|
6527
6568
|
});
|
|
6528
6569
|
},
|
|
6570
|
+
formatBrush(editor, marks) {
|
|
6571
|
+
return TableEditor.handleSelectedCells(editor, (cellPath, cellRange) => {
|
|
6572
|
+
setMarks(editor, marks, cellRange);
|
|
6573
|
+
});
|
|
6574
|
+
},
|
|
6529
6575
|
handleSelectedCells(editor, handle) {
|
|
6530
6576
|
const cells = TableEditor.getSelectedCells(editor);
|
|
6531
6577
|
if (cells) {
|
|
@@ -10908,23 +10954,17 @@ const createTrailingNodePlugin = createPluginFactory({
|
|
|
10908
10954
|
});
|
|
10909
10955
|
|
|
10910
10956
|
const PaintFormatEditor = {
|
|
10911
|
-
formatBrush(editor) {
|
|
10912
|
-
const contextService = editor.injector.get(TheContextService);
|
|
10913
|
-
const obj = {};
|
|
10914
|
-
for (const key of MarkProps) {
|
|
10915
|
-
const k = contextService.paintFormatStatus.marks[key];
|
|
10916
|
-
obj[key] = k || null;
|
|
10917
|
-
}
|
|
10957
|
+
formatBrush(editor, marks) {
|
|
10918
10958
|
const block = anchorBlock(editor);
|
|
10919
10959
|
if (block && Range.isCollapsed(editor.selection)) {
|
|
10920
10960
|
// TODO:: 在撤销时有bug, 临时使用withoutSaving处理
|
|
10921
10961
|
HistoryEditor.withoutSaving(editor, () => {
|
|
10922
10962
|
const path = TheEditor.findPath(editor, block);
|
|
10923
|
-
setMarks(editor,
|
|
10963
|
+
setMarks(editor, marks, path);
|
|
10924
10964
|
});
|
|
10925
10965
|
}
|
|
10926
10966
|
else {
|
|
10927
|
-
setMarks(editor,
|
|
10967
|
+
setMarks(editor, marks);
|
|
10928
10968
|
}
|
|
10929
10969
|
PaintFormatEditor.cancelFormatBrushStatus(editor);
|
|
10930
10970
|
},
|
|
@@ -10948,7 +10988,16 @@ const PaintFormatEditor = {
|
|
|
10948
10988
|
.pipe(filter(event => element.contains(event.target)), take(1))
|
|
10949
10989
|
.subscribe((event) => {
|
|
10950
10990
|
if (contextService.paintFormatStatus.isActive) {
|
|
10951
|
-
|
|
10991
|
+
const contextService = editor.injector.get(TheContextService);
|
|
10992
|
+
const marks = {};
|
|
10993
|
+
for (const key of MarkProps) {
|
|
10994
|
+
marks[key] = contextService.paintFormatStatus.marks[key] || null;
|
|
10995
|
+
}
|
|
10996
|
+
if (TableEditor.formatBrush(editor, marks)) {
|
|
10997
|
+
PaintFormatEditor.cancelFormatBrushStatus(editor);
|
|
10998
|
+
return;
|
|
10999
|
+
}
|
|
11000
|
+
PaintFormatEditor.formatBrush(editor, marks);
|
|
10952
11001
|
}
|
|
10953
11002
|
});
|
|
10954
11003
|
editor.onChange();
|
|
@@ -11148,6 +11197,69 @@ const createSoftBreakPlugin = createPluginFactory({
|
|
|
11148
11197
|
}
|
|
11149
11198
|
});
|
|
11150
11199
|
|
|
11200
|
+
class TheTableRowComponent extends TheBaseElementComponent {
|
|
11201
|
+
constructor(elementRef, cdr, renderer) {
|
|
11202
|
+
super(elementRef, cdr);
|
|
11203
|
+
this.elementRef = elementRef;
|
|
11204
|
+
this.cdr = cdr;
|
|
11205
|
+
this.renderer = renderer;
|
|
11206
|
+
this.numberedColumn = false;
|
|
11207
|
+
this.rowNumber = '';
|
|
11208
|
+
}
|
|
11209
|
+
onContextChange() {
|
|
11210
|
+
super.onContextChange();
|
|
11211
|
+
const path = TheEditor.findPath(this.editor, this.element);
|
|
11212
|
+
const tablePosition = createTablePosition(this.editor, path.concat(0));
|
|
11213
|
+
const rowIndex = tablePosition.getRowIndex();
|
|
11214
|
+
if (rowIndex === 0) {
|
|
11215
|
+
this.renderer.addClass(this.elementRef.nativeElement, 'the-sticky-row');
|
|
11216
|
+
}
|
|
11217
|
+
else {
|
|
11218
|
+
this.renderer.removeClass(this.elementRef.nativeElement, 'the-sticky-row');
|
|
11219
|
+
}
|
|
11220
|
+
if (this.initialized) {
|
|
11221
|
+
this.useHeight();
|
|
11222
|
+
}
|
|
11223
|
+
this.setNumberColumn(tablePosition, rowIndex);
|
|
11224
|
+
}
|
|
11225
|
+
ngOnInit() {
|
|
11226
|
+
super.ngOnInit();
|
|
11227
|
+
this.useHeight();
|
|
11228
|
+
}
|
|
11229
|
+
useHeight() {
|
|
11230
|
+
if (this.element.height) {
|
|
11231
|
+
this.height = this.element.height + 'px';
|
|
11232
|
+
}
|
|
11233
|
+
}
|
|
11234
|
+
setNumberColumn(ps, rowIndex) {
|
|
11235
|
+
const headerRow = isHeaderRow(ps.table);
|
|
11236
|
+
this.numberedColumn = ps.table?.options?.numberedColumn;
|
|
11237
|
+
this.rowNumber = headerRow && rowIndex === 0 ? '' : headerRow ? rowIndex : rowIndex + 1;
|
|
11238
|
+
}
|
|
11239
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.5", ngImport: i0, type: TheTableRowComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11240
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.5", type: TheTableRowComponent, selector: "tr[theTableRow]", host: { properties: { "style.height": "this.height" } }, usesInheritance: true, ngImport: i0, template: `
|
|
11241
|
+
<td *ngIf="readonly && numberedColumn" class="the-table-number-column align-middle" thePreventDefault>
|
|
11242
|
+
{{ rowNumber }}
|
|
11243
|
+
</td>
|
|
11244
|
+
<slate-children [children]="children" [context]="childrenContext" [viewContext]="viewContext"></slate-children>
|
|
11245
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.SlateChildren, selector: "slate-children", inputs: ["children", "context", "viewContext"] }, { kind: "directive", type: ThePreventDefaultDirective, selector: "[thePreventDefault]", exportAs: ["thePreventDefault"] }] }); }
|
|
11246
|
+
}
|
|
11247
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.5", ngImport: i0, type: TheTableRowComponent, decorators: [{
|
|
11248
|
+
type: Component,
|
|
11249
|
+
args: [{
|
|
11250
|
+
selector: 'tr[theTableRow]',
|
|
11251
|
+
template: `
|
|
11252
|
+
<td *ngIf="readonly && numberedColumn" class="the-table-number-column align-middle" thePreventDefault>
|
|
11253
|
+
{{ rowNumber }}
|
|
11254
|
+
</td>
|
|
11255
|
+
<slate-children [children]="children" [context]="childrenContext" [viewContext]="viewContext"></slate-children>
|
|
11256
|
+
`
|
|
11257
|
+
}]
|
|
11258
|
+
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }]; }, propDecorators: { height: [{
|
|
11259
|
+
type: HostBinding,
|
|
11260
|
+
args: ['style.height']
|
|
11261
|
+
}] } });
|
|
11262
|
+
|
|
11151
11263
|
class TheToolbarGroupComponent {
|
|
11152
11264
|
set item(i) {
|
|
11153
11265
|
this._item = i;
|
|
@@ -12149,27 +12261,10 @@ class TableStore {
|
|
|
12149
12261
|
}
|
|
12150
12262
|
}
|
|
12151
12263
|
selectCellInDirection(direction) {
|
|
12152
|
-
const pos = createTablePosition(this.editor);
|
|
12153
12264
|
const [row, col] = this.focusCellPath.slice(-2);
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
nextCellPath = [row - 1, col];
|
|
12158
|
-
break;
|
|
12159
|
-
case TableSelectCellDirection.right:
|
|
12160
|
-
nextCellPath = [row, col + 1];
|
|
12161
|
-
break;
|
|
12162
|
-
case TableSelectCellDirection.down:
|
|
12163
|
-
nextCellPath = [row + 1, col];
|
|
12164
|
-
break;
|
|
12165
|
-
case TableSelectCellDirection.left:
|
|
12166
|
-
nextCellPath = [row, col - 1];
|
|
12167
|
-
break;
|
|
12168
|
-
}
|
|
12169
|
-
const nextCellPosition = { row: nextCellPath[0], col: nextCellPath[1] };
|
|
12170
|
-
const cell = pos.findCellByPath(nextCellPosition);
|
|
12171
|
-
if (cell) {
|
|
12172
|
-
this.setAreaSelectionCells(cell);
|
|
12265
|
+
const nextCell = getNextCell(this.editor, direction, [row, col]);
|
|
12266
|
+
if (nextCell) {
|
|
12267
|
+
this.setAreaSelectionCells(nextCell);
|
|
12173
12268
|
}
|
|
12174
12269
|
}
|
|
12175
12270
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.5", ngImport: i0, type: TableStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
@@ -12871,6 +12966,113 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.5", ngImpor
|
|
|
12871
12966
|
type: Injectable
|
|
12872
12967
|
}], ctorParameters: function () { return [{ type: i1$1.ThyPopover }, { type: i2$1.Overlay }, { type: TableStore }, { type: i0.NgZone }, { type: TheContextService }]; } });
|
|
12873
12968
|
|
|
12969
|
+
function calculateAnchorPositionInCell(editor, at) {
|
|
12970
|
+
at = at || editor.selection.anchor;
|
|
12971
|
+
const entry = anchorBlockEntry(editor, at);
|
|
12972
|
+
const [block, blockPath] = entry;
|
|
12973
|
+
const blockDom = AngularEditor.toDOMNode(editor, block);
|
|
12974
|
+
const valueRangeRect = blockDom.getBoundingClientRect();
|
|
12975
|
+
const blockHeight = window.getComputedStyle(blockDom).lineHeight;
|
|
12976
|
+
const nativeSelection = window.getSelection();
|
|
12977
|
+
const nativeRange = nativeSelection && nativeSelection.getRangeAt(0);
|
|
12978
|
+
const nativeRangeRect = nativeRange && nativeRange.getBoundingClientRect();
|
|
12979
|
+
let isFirstLine = false;
|
|
12980
|
+
let isLastLine = false;
|
|
12981
|
+
if (valueRangeRect && nativeRangeRect && blockHeight) {
|
|
12982
|
+
const lineHeight = parseInt(blockHeight, 10);
|
|
12983
|
+
isFirstLine = nativeRangeRect.top - valueRangeRect.top < lineHeight;
|
|
12984
|
+
isLastLine = valueRangeRect.bottom - nativeRangeRect.bottom < lineHeight;
|
|
12985
|
+
}
|
|
12986
|
+
return {
|
|
12987
|
+
isFirstLine,
|
|
12988
|
+
isLastLine,
|
|
12989
|
+
blockPath
|
|
12990
|
+
};
|
|
12991
|
+
}
|
|
12992
|
+
|
|
12993
|
+
const keyShiftDir = {
|
|
12994
|
+
'shift+up': TableSelectCellDirection.up,
|
|
12995
|
+
'shift+right': TableSelectCellDirection.right,
|
|
12996
|
+
'shift+down': TableSelectCellDirection.down,
|
|
12997
|
+
'shift+left': TableSelectCellDirection.left
|
|
12998
|
+
};
|
|
12999
|
+
const getHotKey = (e) => {
|
|
13000
|
+
const hotkey = ['shift+up', 'shift+right', 'shift+down', 'shift+left'].find(key => isKeyHotkey(key, e));
|
|
13001
|
+
return hotkey;
|
|
13002
|
+
};
|
|
13003
|
+
/**
|
|
13004
|
+
* 处理单元格内 shift + up/down/left/right 行为
|
|
13005
|
+
* @param editor 编辑器对象
|
|
13006
|
+
* @param e 键盘事件对象
|
|
13007
|
+
*/
|
|
13008
|
+
const moveSelectionFromCell = (editor, e) => {
|
|
13009
|
+
const hotkey = getHotKey(e);
|
|
13010
|
+
if (!hotkey) {
|
|
13011
|
+
return;
|
|
13012
|
+
}
|
|
13013
|
+
const { selection } = editor;
|
|
13014
|
+
const cursor = selection.focus;
|
|
13015
|
+
const tablePosition = createTablePosition(editor);
|
|
13016
|
+
const tableComponent = ELEMENT_TO_COMPONENT.get(tablePosition.table);
|
|
13017
|
+
const tableStore = tableComponent.tableStore;
|
|
13018
|
+
const selectedCells = tableStore.getSelectedCellPositions();
|
|
13019
|
+
const endPosition = createTablePosition(editor, cursor.path);
|
|
13020
|
+
const cellPath = endPosition.cellEntry[1];
|
|
13021
|
+
const isStart = Editor.isStart(editor, selection.focus, cellPath);
|
|
13022
|
+
const isEnd = Editor.isEnd(editor, selection.focus, cellPath);
|
|
13023
|
+
const isUp = isKeyHotkey('shift+up', e);
|
|
13024
|
+
const isDown = isKeyHotkey('shift+down', e);
|
|
13025
|
+
const isLeft = isKeyHotkey('shift+left', e);
|
|
13026
|
+
const isRight = isKeyHotkey('shift+right', e);
|
|
13027
|
+
// 当前已经没有选中单元格
|
|
13028
|
+
if (selectedCells.length === 0) {
|
|
13029
|
+
if (isUp || isDown) {
|
|
13030
|
+
const cellStartPath = Editor.start(editor, cellPath).path;
|
|
13031
|
+
const cellEndPath = Editor.end(editor, cellPath).path;
|
|
13032
|
+
const { isFirstLine, isLastLine } = calculateAnchorPositionInCell(editor, cursor);
|
|
13033
|
+
const [, anchorBlockPath] = anchorBlockEntry(editor, cursor);
|
|
13034
|
+
const anchorPosition = createTablePosition(editor, selection.anchor.path);
|
|
13035
|
+
const startCellPosition = createTablePosition(editor, cellStartPath);
|
|
13036
|
+
const endCellPosition = createTablePosition(editor, cellEndPath);
|
|
13037
|
+
const isSameCellByStart = Path.equals(anchorPosition.cellEntry[1], startCellPosition.cellEntry[1]);
|
|
13038
|
+
const isSameCellByEnd = Path.equals(anchorPosition.cellEntry[1], endCellPosition.cellEntry[1]);
|
|
13039
|
+
cellStartPath.pop();
|
|
13040
|
+
cellEndPath.pop();
|
|
13041
|
+
// 同一单元格内,当前光标不在首行或当前是跨行,则执行默认逻辑
|
|
13042
|
+
if (isUp && isSameCellByStart && (!isFirstLine || !Path.equals(anchorBlockPath, cellStartPath))) {
|
|
13043
|
+
return;
|
|
13044
|
+
}
|
|
13045
|
+
// 同一单元格内,当前光标不在最后一行或当前是跨行,则执行默认逻辑
|
|
13046
|
+
if (isDown && isSameCellByEnd && (!isLastLine || !Path.equals(anchorBlockPath, cellEndPath))) {
|
|
13047
|
+
return;
|
|
13048
|
+
}
|
|
13049
|
+
}
|
|
13050
|
+
if (isLeft) {
|
|
13051
|
+
const beforePoint = Editor.before(editor, selection);
|
|
13052
|
+
const beforePosition = beforePoint && createTablePosition(editor, beforePoint.path);
|
|
13053
|
+
// 如果不是单元格或不在起始位置,则不处理
|
|
13054
|
+
if (!beforePosition?.cell || !isStart) {
|
|
13055
|
+
return;
|
|
13056
|
+
}
|
|
13057
|
+
}
|
|
13058
|
+
if (isRight) {
|
|
13059
|
+
const afterPoint = Editor.after(editor, selection);
|
|
13060
|
+
const afterPosition = afterPoint && createTablePosition(editor, afterPoint.path);
|
|
13061
|
+
// 如果不是单元格或不是单元格尾部,则不处理
|
|
13062
|
+
if (!afterPosition?.cell || !isEnd) {
|
|
13063
|
+
return;
|
|
13064
|
+
}
|
|
13065
|
+
}
|
|
13066
|
+
}
|
|
13067
|
+
tableStore.selectCellInDirection(keyShiftDir[hotkey]);
|
|
13068
|
+
setTimeout(() => {
|
|
13069
|
+
const tableService = tableComponent.tableService;
|
|
13070
|
+
const isMobile = isMobileMode(editor);
|
|
13071
|
+
const focusElement = tableComponent.tbodyNativeElement?.querySelector('.focused-cell') || tableStore.focusCellElement;
|
|
13072
|
+
tableService.afterSelectedCells(focusElement, tableComponent.element, isMobile);
|
|
13073
|
+
});
|
|
13074
|
+
};
|
|
13075
|
+
|
|
12874
13076
|
const TABLE_SELECTOR = '.the-table';
|
|
12875
13077
|
const TABLE_WRAPPER_SELECTOR = '.the-table-wrapper';
|
|
12876
13078
|
const RESIZE_OVERLAY_SELECTOR = '.the-table-resize-overlay-thumb';
|
|
@@ -13085,9 +13287,6 @@ class TheTableComponent extends TheBaseElementComponent {
|
|
|
13085
13287
|
Promise.resolve().then(() => {
|
|
13086
13288
|
this.tableStore.emitTableChange();
|
|
13087
13289
|
this.tableStore.setSelectedColumnsAndRowIndex();
|
|
13088
|
-
if (!this.tableStore.selectedCells.length && this.tableService.isOpened) {
|
|
13089
|
-
this.tableService.closeToolbar();
|
|
13090
|
-
}
|
|
13091
13290
|
this.bindTableScrollingShadow();
|
|
13092
13291
|
this.useRowControls();
|
|
13093
13292
|
this.setHeaderCellStyle();
|
|
@@ -13157,8 +13356,11 @@ class TheTableComponent extends TheBaseElementComponent {
|
|
|
13157
13356
|
this.tableStore
|
|
13158
13357
|
.selectedCellsChange()
|
|
13159
13358
|
.pipe(takeUntil(this.destroy$))
|
|
13160
|
-
.subscribe(
|
|
13359
|
+
.subscribe(cells => {
|
|
13161
13360
|
this.isSelectedAllCell = isSelectedAllCell(this.editor, this.tableStore.getSelectedCellPositions());
|
|
13361
|
+
if (!cells.length && this.tableService.isOpened) {
|
|
13362
|
+
this.tableService.closeToolbar();
|
|
13363
|
+
}
|
|
13162
13364
|
});
|
|
13163
13365
|
}
|
|
13164
13366
|
getWrapperWidth() {
|
|
@@ -13700,17 +13902,13 @@ class TheTableComponent extends TheBaseElementComponent {
|
|
|
13700
13902
|
if (isKeyHotkey('shift', e)) {
|
|
13701
13903
|
this.tableService.handleKeydownForAreaSelection(e, this.tbodyNativeElement);
|
|
13702
13904
|
}
|
|
13703
|
-
|
|
13704
|
-
|
|
13905
|
+
// 处理单元格内 shift + up/down/left/right 行为
|
|
13906
|
+
if (this.isInTable) {
|
|
13907
|
+
moveSelectionFromCell(this.editor, e);
|
|
13705
13908
|
}
|
|
13706
|
-
|
|
13707
|
-
|
|
13708
|
-
|
|
13709
|
-
if (isKeyHotkey('shift+left', e)) {
|
|
13710
|
-
this.tableStore.selectCellInDirection(TableSelectCellDirection.left);
|
|
13711
|
-
}
|
|
13712
|
-
if (isKeyHotkey('shift+right', e)) {
|
|
13713
|
-
this.tableStore.selectCellInDirection(TableSelectCellDirection.right);
|
|
13909
|
+
// 触发上下左右键时,清除选中的单元格
|
|
13910
|
+
if (isKeyHotkey(['up', 'down', 'left', 'right'], e) && this.tableStore.selectCells?.length > 0) {
|
|
13911
|
+
this.tableStore.clearSelectedCells();
|
|
13714
13912
|
}
|
|
13715
13913
|
});
|
|
13716
13914
|
});
|
|
@@ -13884,116 +14082,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.5", ngImpor
|
|
|
13884
14082
|
args: ['mousedown', ['$event']]
|
|
13885
14083
|
}] } });
|
|
13886
14084
|
|
|
13887
|
-
/**
|
|
13888
|
-
* Clear the content of the given node
|
|
13889
|
-
*/
|
|
13890
|
-
function clearTableNode(opts, editor, isTopToBot) {
|
|
13891
|
-
const { selection } = editor;
|
|
13892
|
-
const element = createTablePosition(editor, selection.focus.path);
|
|
13893
|
-
const rowIndex = element.getRowIndex();
|
|
13894
|
-
const colIndex = element.getColumnIndex();
|
|
13895
|
-
const removeRow = isTopToBot ? 0 : rowIndex + 1;
|
|
13896
|
-
for (let i = 0; i < element.getWidth(); i++) {
|
|
13897
|
-
const inColRange = Range.includes(selection, [...element.tableEntry[1], rowIndex, i]);
|
|
13898
|
-
if (inColRange && i !== colIndex) {
|
|
13899
|
-
Transforms.removeNodes(editor, { at: [...element.tableEntry[1], rowIndex, i] });
|
|
13900
|
-
Transforms.insertNodes(editor, createCell(opts), { at: [...element.tableEntry[1], rowIndex, i] });
|
|
13901
|
-
}
|
|
13902
|
-
}
|
|
13903
|
-
for (let i = 0; i < element.getHeight(); i++) {
|
|
13904
|
-
const inRowRange = Range.includes(selection, [...element.tableEntry[1], i]);
|
|
13905
|
-
if (inRowRange && i !== rowIndex) {
|
|
13906
|
-
Transforms.removeNodes(editor, { at: [...element.tableEntry[1], removeRow] });
|
|
13907
|
-
}
|
|
13908
|
-
}
|
|
13909
|
-
}
|
|
13910
|
-
|
|
13911
|
-
function calculateAnchorPositionInCell(editor) {
|
|
13912
|
-
let isFirstLine = false;
|
|
13913
|
-
let isLastLine = false;
|
|
13914
|
-
const anchorBlock$1 = anchorBlock(editor);
|
|
13915
|
-
const anchorBlockPath = findPath(editor, anchorBlock$1);
|
|
13916
|
-
const anchorBlockDom = AngularEditor.toDOMNode(editor, anchorBlock$1);
|
|
13917
|
-
const valueRangeRect = anchorBlockDom.getBoundingClientRect();
|
|
13918
|
-
const anchorBlockHeight = window.getComputedStyle(anchorBlockDom).lineHeight;
|
|
13919
|
-
const nativeSelection = window.getSelection();
|
|
13920
|
-
const nativeRange = nativeSelection && nativeSelection.getRangeAt(0);
|
|
13921
|
-
const nativeRangeRect = nativeRange && nativeRange.getBoundingClientRect();
|
|
13922
|
-
if (valueRangeRect && nativeRangeRect && anchorBlockHeight) {
|
|
13923
|
-
const lineHeight = parseInt(anchorBlockHeight, 10);
|
|
13924
|
-
isFirstLine = nativeRangeRect.top - valueRangeRect.top < lineHeight;
|
|
13925
|
-
isLastLine = valueRangeRect.bottom - nativeRangeRect.bottom < lineHeight;
|
|
13926
|
-
}
|
|
13927
|
-
return {
|
|
13928
|
-
isFirstLine,
|
|
13929
|
-
isLastLine,
|
|
13930
|
-
anchorBlockPath
|
|
13931
|
-
};
|
|
13932
|
-
}
|
|
13933
|
-
|
|
13934
|
-
class TheTableRowComponent extends TheBaseElementComponent {
|
|
13935
|
-
constructor(elementRef, cdr, renderer) {
|
|
13936
|
-
super(elementRef, cdr);
|
|
13937
|
-
this.elementRef = elementRef;
|
|
13938
|
-
this.cdr = cdr;
|
|
13939
|
-
this.renderer = renderer;
|
|
13940
|
-
this.numberedColumn = false;
|
|
13941
|
-
this.rowNumber = '';
|
|
13942
|
-
}
|
|
13943
|
-
onContextChange() {
|
|
13944
|
-
super.onContextChange();
|
|
13945
|
-
const path = TheEditor.findPath(this.editor, this.element);
|
|
13946
|
-
const tablePosition = createTablePosition(this.editor, path.concat(0));
|
|
13947
|
-
const rowIndex = tablePosition.getRowIndex();
|
|
13948
|
-
if (rowIndex === 0) {
|
|
13949
|
-
this.renderer.addClass(this.elementRef.nativeElement, 'the-sticky-row');
|
|
13950
|
-
}
|
|
13951
|
-
else {
|
|
13952
|
-
this.renderer.removeClass(this.elementRef.nativeElement, 'the-sticky-row');
|
|
13953
|
-
}
|
|
13954
|
-
if (this.initialized) {
|
|
13955
|
-
this.useHeight();
|
|
13956
|
-
}
|
|
13957
|
-
this.setNumberColumn(tablePosition, rowIndex);
|
|
13958
|
-
}
|
|
13959
|
-
ngOnInit() {
|
|
13960
|
-
super.ngOnInit();
|
|
13961
|
-
this.useHeight();
|
|
13962
|
-
}
|
|
13963
|
-
useHeight() {
|
|
13964
|
-
if (this.element.height) {
|
|
13965
|
-
this.height = this.element.height + 'px';
|
|
13966
|
-
}
|
|
13967
|
-
}
|
|
13968
|
-
setNumberColumn(ps, rowIndex) {
|
|
13969
|
-
const headerRow = isHeaderRow(ps.table);
|
|
13970
|
-
this.numberedColumn = ps.table?.options?.numberedColumn;
|
|
13971
|
-
this.rowNumber = headerRow && rowIndex === 0 ? '' : headerRow ? rowIndex : rowIndex + 1;
|
|
13972
|
-
}
|
|
13973
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.5", ngImport: i0, type: TheTableRowComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
13974
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.5", type: TheTableRowComponent, selector: "tr[theTableRow]", host: { properties: { "style.height": "this.height" } }, usesInheritance: true, ngImport: i0, template: `
|
|
13975
|
-
<td *ngIf="readonly && numberedColumn" class="the-table-number-column align-middle" thePreventDefault>
|
|
13976
|
-
{{ rowNumber }}
|
|
13977
|
-
</td>
|
|
13978
|
-
<slate-children [children]="children" [context]="childrenContext" [viewContext]="viewContext"></slate-children>
|
|
13979
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.SlateChildren, selector: "slate-children", inputs: ["children", "context", "viewContext"] }, { kind: "directive", type: ThePreventDefaultDirective, selector: "[thePreventDefault]", exportAs: ["thePreventDefault"] }] }); }
|
|
13980
|
-
}
|
|
13981
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.5", ngImport: i0, type: TheTableRowComponent, decorators: [{
|
|
13982
|
-
type: Component,
|
|
13983
|
-
args: [{
|
|
13984
|
-
selector: 'tr[theTableRow]',
|
|
13985
|
-
template: `
|
|
13986
|
-
<td *ngIf="readonly && numberedColumn" class="the-table-number-column align-middle" thePreventDefault>
|
|
13987
|
-
{{ rowNumber }}
|
|
13988
|
-
</td>
|
|
13989
|
-
<slate-children [children]="children" [context]="childrenContext" [viewContext]="viewContext"></slate-children>
|
|
13990
|
-
`
|
|
13991
|
-
}]
|
|
13992
|
-
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }]; }, propDecorators: { height: [{
|
|
13993
|
-
type: HostBinding,
|
|
13994
|
-
args: ['style.height']
|
|
13995
|
-
}] } });
|
|
13996
|
-
|
|
13997
14085
|
/**
|
|
13998
14086
|
* @license
|
|
13999
14087
|
* Copyright Google LLC All Rights Reserved.
|
|
@@ -14976,36 +15064,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.5", ngImpor
|
|
|
14976
15064
|
args: ['style.display']
|
|
14977
15065
|
}] } });
|
|
14978
15066
|
|
|
14979
|
-
const normalizeTable = (table) => {
|
|
14980
|
-
const normalizedNodes = [];
|
|
14981
|
-
const rowHeight = table.children.length;
|
|
14982
|
-
const columnWidth = table.children[0].children.length;
|
|
14983
|
-
table.children.forEach((row, rowIndex) => {
|
|
14984
|
-
row.children.forEach((cell, columnIndex) => {
|
|
14985
|
-
// case 1
|
|
14986
|
-
if (cell.colspan || cell.rowspan) {
|
|
14987
|
-
const rowspan = cell.rowspan || 1;
|
|
14988
|
-
const colspan = cell.colspan || 1;
|
|
14989
|
-
if (rowspan > rowHeight - rowIndex) {
|
|
14990
|
-
cell.rowspan = rowHeight - rowIndex;
|
|
14991
|
-
}
|
|
14992
|
-
if (colspan > columnWidth - columnIndex) {
|
|
14993
|
-
cell.colspan = columnWidth - columnIndex;
|
|
14994
|
-
}
|
|
14995
|
-
return;
|
|
14996
|
-
}
|
|
14997
|
-
// case 2
|
|
14998
|
-
if (cell.hidden && !normalizedNodes.includes(cell)) {
|
|
14999
|
-
const origin = getOriginCell(table, rowIndex, columnIndex);
|
|
15000
|
-
if (!origin) {
|
|
15001
|
-
delete table.children[rowIndex].children[columnIndex].hidden;
|
|
15002
|
-
}
|
|
15003
|
-
}
|
|
15004
|
-
});
|
|
15005
|
-
});
|
|
15006
|
-
return table;
|
|
15007
|
-
};
|
|
15008
|
-
|
|
15009
15067
|
class TheTableToolbarItemComponent extends TheBaseToolbarItem {
|
|
15010
15068
|
get isOpenTableSelect() {
|
|
15011
15069
|
return this.tableSelectRef && this.tableSelectRef.componentInstance;
|
|
@@ -15090,6 +15148,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.5", ngImpor
|
|
|
15090
15148
|
}]
|
|
15091
15149
|
}], ctorParameters: function () { return [{ type: i1$1.ThyPopover }, { type: i2$1.Overlay }]; } });
|
|
15092
15150
|
|
|
15151
|
+
/**
|
|
15152
|
+
* Clear the content of the given node
|
|
15153
|
+
*/
|
|
15154
|
+
function clearTableNode(opts, editor, isTopToBot) {
|
|
15155
|
+
const { selection } = editor;
|
|
15156
|
+
const element = createTablePosition(editor, selection.focus.path);
|
|
15157
|
+
const rowIndex = element.getRowIndex();
|
|
15158
|
+
const colIndex = element.getColumnIndex();
|
|
15159
|
+
const removeRow = isTopToBot ? 0 : rowIndex + 1;
|
|
15160
|
+
for (let i = 0; i < element.getWidth(); i++) {
|
|
15161
|
+
const inColRange = Range.includes(selection, [...element.tableEntry[1], rowIndex, i]);
|
|
15162
|
+
if (inColRange && i !== colIndex) {
|
|
15163
|
+
Transforms.removeNodes(editor, { at: [...element.tableEntry[1], rowIndex, i] });
|
|
15164
|
+
Transforms.insertNodes(editor, createCell(opts), { at: [...element.tableEntry[1], rowIndex, i] });
|
|
15165
|
+
}
|
|
15166
|
+
}
|
|
15167
|
+
for (let i = 0; i < element.getHeight(); i++) {
|
|
15168
|
+
const inRowRange = Range.includes(selection, [...element.tableEntry[1], i]);
|
|
15169
|
+
if (inRowRange && i !== rowIndex) {
|
|
15170
|
+
Transforms.removeNodes(editor, { at: [...element.tableEntry[1], removeRow] });
|
|
15171
|
+
}
|
|
15172
|
+
}
|
|
15173
|
+
}
|
|
15174
|
+
|
|
15093
15175
|
const isLegalTable = (editor, element, path) => {
|
|
15094
15176
|
try {
|
|
15095
15177
|
const nodePath = path || TheEditor.findPath(editor, element);
|
|
@@ -15102,6 +15184,36 @@ const isLegalTable = (editor, element, path) => {
|
|
|
15102
15184
|
}
|
|
15103
15185
|
};
|
|
15104
15186
|
|
|
15187
|
+
const normalizeTable = (table) => {
|
|
15188
|
+
const normalizedNodes = [];
|
|
15189
|
+
const rowHeight = table.children.length;
|
|
15190
|
+
const columnWidth = table.children[0].children.length;
|
|
15191
|
+
table.children.forEach((row, rowIndex) => {
|
|
15192
|
+
row.children.forEach((cell, columnIndex) => {
|
|
15193
|
+
// case 1
|
|
15194
|
+
if (cell.colspan || cell.rowspan) {
|
|
15195
|
+
const rowspan = cell.rowspan || 1;
|
|
15196
|
+
const colspan = cell.colspan || 1;
|
|
15197
|
+
if (rowspan > rowHeight - rowIndex) {
|
|
15198
|
+
cell.rowspan = rowHeight - rowIndex;
|
|
15199
|
+
}
|
|
15200
|
+
if (colspan > columnWidth - columnIndex) {
|
|
15201
|
+
cell.colspan = columnWidth - columnIndex;
|
|
15202
|
+
}
|
|
15203
|
+
return;
|
|
15204
|
+
}
|
|
15205
|
+
// case 2
|
|
15206
|
+
if (cell.hidden && !normalizedNodes.includes(cell)) {
|
|
15207
|
+
const origin = getOriginCell(table, rowIndex, columnIndex);
|
|
15208
|
+
if (!origin) {
|
|
15209
|
+
delete table.children[rowIndex].children[columnIndex].hidden;
|
|
15210
|
+
}
|
|
15211
|
+
}
|
|
15212
|
+
});
|
|
15213
|
+
});
|
|
15214
|
+
return table;
|
|
15215
|
+
};
|
|
15216
|
+
|
|
15105
15217
|
const withTable = (editor) => {
|
|
15106
15218
|
const { deleteBackward, deleteForward, onKeydown, setFragmentData, insertData, normalizeNode, isBlockCard, renderElement, deleteCutData, isContainer, onChange, onClick } = editor;
|
|
15107
15219
|
editor.deleteBackward = unit => {
|