@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.
@@ -3376,49 +3376,185 @@ var TableSelectCellDirection;
3376
3376
  TableSelectCellDirection[TableSelectCellDirection["left"] = 3] = "left";
3377
3377
  })(TableSelectCellDirection || (TableSelectCellDirection = {}));
3378
3378
 
3379
- /**
3380
- * Create a new cell
3381
- */
3382
- function createCell(opts, nodes) {
3383
- return {
3384
- type: opts.typeCell,
3385
- children: nodes || [createEmptyContent(opts)]
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
- * Create a new default content block
3390
- */
3391
- function createEmptyContent(opts) {
3392
- return {
3393
- type: opts.typeContent,
3394
- children: [{ text: '' }]
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
- * Create a new row block
3453
+ * 计算最小行跨距单元格
3454
+ * @param element TableElement
3455
+ * @returns
3400
3456
  */
3401
- function createRow(opts, columns, getCellContent) {
3402
- const cellNodes = new Array(columns).fill(1).map(i => createCell(opts, getCellContent ? getCellContent(i) : undefined));
3403
- return {
3404
- type: opts.typeRow,
3405
- children: cellNodes
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
- * Create a table
3479
+ * 计算行控件的平均高度
3480
+ * @param previousCombineRowIndex
3481
+ * @param previousRowIndex
3482
+ * @param rowControls
3411
3483
  */
3412
- function createTable(opts, columns, rows, getCellContent) {
3413
- const rowNodes = new Array(rows).fill(1).map(i => createRow(opts, columns, getCellContent ? getCellContent.bind(null, i) : undefined));
3414
- return {
3415
- type: opts.typeTable,
3416
- children: rowNodes,
3417
- options: {
3418
- headerRow: true
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.anchor?.path;
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
- * True if the given range is inside one table
3809
+ * 判断是否选中了所有的单元格
3810
+ * @param editor 编辑器对象
3811
+ * @param selectedCellPositions 选中的单元格位置数组
3812
+ * @returns 是否选中了所有的单元格
3673
3813
  */
3674
- function isRangeInTable(opts, editor, range) {
3675
- const { focus } = range;
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
- // Inside the same table
3683
- return startPosition.table === endPosition.table;
3684
- }
3685
-
3818
+ const pos = createTablePosition(editor);
3819
+ return !!selectedCellPositions.length && pos.getHeight() * pos.getWidth() === selectedCellPositions.length;
3820
+ };
3686
3821
  /**
3687
- * Is the selection in a table
3822
+ * 获取选中的单元格位置数组
3823
+ * @param editor 编辑器对象
3824
+ * @param selectedCells 选中的单元格
3825
+ * @returns 选中的单元格位置
3688
3826
  */
3689
- function isSelectionInTable(opts, editor) {
3690
- if (!editor.selection) {
3691
- return false;
3692
- }
3693
- return isRangeInTable(opts, editor, editor.selection);
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 editor
3698
- * @param cells
3699
- * @returns boolean
3835
+ * 获取一定范围内所有的单元格位置
3836
+ * @param startRow 起始行
3837
+ * @param startCol 起始列
3838
+ * @param endRow 结束行
3839
+ * @param endCol 结束列
3840
+ * @returns 单元格位置
3700
3841
  */
3701
- function isRectangularInTableCells(editor, cells) {
3702
- let selectCellSize = cells.length;
3703
- if (selectCellSize === 0) {
3704
- return false;
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 (maxRow + 1 - minRow) * (maxCol + 1 - minCol) === selectCellSize;
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
- function calcSpanForRow(table, targetIndex) {
3736
- const rowspans = [];
3737
- table.children
3738
- .filter((row, rowIndex) => rowIndex < targetIndex)
3739
- .forEach((row, rowIndex) => {
3740
- row.children.forEach((cell, columnIndex) => {
3741
- if (cell.rowspan && cell.rowspan > 1) {
3742
- const rect = {
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
- function getOriginCell(table, targetRowIndex, targetColumnIndex) {
3778
- for (let rowIndex = 0; rowIndex <= targetRowIndex; rowIndex++) {
3779
- const row = table.children[rowIndex];
3780
- const originCell = row.children
3781
- .filter((cell, columnIndex) => columnIndex <= targetColumnIndex)
3782
- .find((cell, columnIndex) => {
3783
- if (cell.rowspan > 1 || cell.colspan > 1) {
3784
- const rect = {
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
- function isInside(cellRect, rowIndex, columnIndex) {
3799
- if (rowIndex >= cellRect.x &&
3800
- rowIndex <= cellRect.x + cellRect.height - 1 &&
3801
- columnIndex >= cellRect.y &&
3802
- columnIndex <= cellRect.y + cellRect.width - 1) {
3803
- return true;
3804
- }
3805
- return false;
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
- function isVirtualKey(e) {
3809
- const isMod = e.ctrlKey || e.metaKey;
3810
- const isAlt = isKeyHotkey('alt', e);
3811
- const isShift = isKeyHotkey('shift', e);
3812
- const isCapsLock = e.key.includes('CapsLock');
3813
- const isTab = e.key.includes('Tab');
3814
- const isEsc = e.key.includes('Escape');
3815
- const isF = e.key.startsWith('F');
3816
- const isArrow = e.key.includes('Arrow') ? true : false;
3817
- return isCapsLock || isMod || isAlt || isArrow || isShift || isTab || isEsc || isF;
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
- const cellDom = AngularEditor.toDOMNode(editor, cellInfo.cell);
4084
- let height = getElementHeight(cellDom);
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
- else {
4094
- rowControls.push({
4095
- height,
4096
- rowIndex: cellInfo.rowIndex
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
- previousRowIndex = index;
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
- * compatible with old data
4106
- * @returns
3999
+ * 打印模式下,按照原宽度比例基于当前表格宽度计算列宽
4000
+ * 1. 所有列的最小列宽总和大于表格宽度时,所有列返回最小宽度
4001
+ * @param element
4002
+ * @param minWidthPx
4003
+ * @returns number[]
4107
4004
  */
4108
- function isHeaderRow(element) {
4109
- if (element?.options?.headerRow) {
4110
- return true;
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
- // compat old data
4113
- if (element?.children[0].header) {
4114
- return true;
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, obj, path);
10963
+ setMarks(editor, marks, path);
10924
10964
  });
10925
10965
  }
10926
10966
  else {
10927
- setMarks(editor, obj);
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
- PaintFormatEditor.formatBrush(editor);
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
- let nextCellPath = [row, col];
12155
- switch (direction) {
12156
- case TableSelectCellDirection.up:
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
- if (isKeyHotkey('shift+up', e)) {
13704
- this.tableStore.selectCellInDirection(TableSelectCellDirection.up);
13905
+ // 处理单元格内 shift + up/down/left/right 行为
13906
+ if (this.isInTable) {
13907
+ moveSelectionFromCell(this.editor, e);
13705
13908
  }
13706
- if (isKeyHotkey('shift+down', e)) {
13707
- this.tableStore.selectCellInDirection(TableSelectCellDirection.down);
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 => {