@ckeditor/ckeditor5-table 48.3.1 → 48.4.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/ckeditor5-metadata.json +12 -1
  2. package/dist/augmentation.d.ts +4 -1
  3. package/dist/commands/inserttablecommand.d.ts +5 -0
  4. package/dist/commands/inserttablelayoutcommand.d.ts +5 -0
  5. package/dist/commands/splitcellcommand.d.ts +2 -1
  6. package/dist/index-content.css +81 -82
  7. package/dist/index-content.css.map +1 -0
  8. package/dist/index-editor.css +537 -450
  9. package/dist/index-editor.css.map +1 -0
  10. package/dist/index.css +214 -125
  11. package/dist/index.css.map +1 -1
  12. package/dist/index.d.ts +6 -1
  13. package/dist/index.js +908 -60
  14. package/dist/index.js.map +1 -1
  15. package/dist/table.d.ts +0 -1
  16. package/dist/tablecaption.d.ts +0 -1
  17. package/dist/tablecellproperties/tablecellpropertiesui.d.ts +7 -0
  18. package/dist/tablecellproperties/ui/tablecellpropertiesview.d.ts +0 -3
  19. package/dist/tablecolumnresize/commands/tablecolumnwidthcommand.d.ts +56 -0
  20. package/dist/tablecolumnresize/constants.d.ts +15 -0
  21. package/dist/tablecolumnresize/tablecolumnresizeediting.d.ts +81 -2
  22. package/dist/tablecolumnresize/utils.d.ts +30 -0
  23. package/dist/tablecolumnresize.d.ts +0 -1
  24. package/dist/tableconfig.d.ts +38 -0
  25. package/dist/tableediting.d.ts +0 -1
  26. package/dist/tablelayout/tablelayoutediting.d.ts +0 -1
  27. package/dist/tableproperties/ui/tablepropertiesview.d.ts +0 -3
  28. package/dist/tablescroll/tablescrollediting.d.ts +82 -0
  29. package/dist/tablescroll/watchers.d.ts +23 -0
  30. package/dist/tablescroll.d.ts +23 -0
  31. package/dist/tableselection.d.ts +0 -1
  32. package/dist/ui/colorinputview.d.ts +0 -1
  33. package/dist/ui/inserttableview.d.ts +0 -1
  34. package/dist/utils/common.d.ts +6 -0
  35. package/package.json +10 -9
package/dist/index.js CHANGED
@@ -6,10 +6,11 @@ import { Command, Plugin } from "@ckeditor/ckeditor5-core";
6
6
  import { Widget, WidgetToolbarRepository, isWidget, toWidget, toWidgetEditable } from "@ckeditor/ckeditor5-widget";
7
7
  import { CKEditorError, Collection, DomEmitterMixin, FocusTracker, KeystrokeHandler, Rect, first, getLocalizedArrowKeyCodeDirection, global, priorities, toUnit, uid } from "@ckeditor/ckeditor5-utils";
8
8
  import { debounce, isEqual, isObject, throttle } from "es-toolkit/compat";
9
+ import { DomEventObserver, Matcher, ModelDocumentSelection, ModelElement, addBackgroundStylesRules, addBorderStylesRules, addMarginStylesRules, addPaddingStylesRules, enableViewPlaceholder, isColorStyleValue, isLengthStyleValue, isPercentageStyleValue } from "@ckeditor/ckeditor5-engine";
10
+ import { _getCopyOnEnterAttributes } from "@ckeditor/ckeditor5-enter";
9
11
  import { IconAlignBottom, IconAlignCenter, IconAlignJustify, IconAlignLeft, IconAlignMiddle, IconAlignRight, IconAlignTop, IconCaption, IconObjectCenter, IconObjectInlineLeft, IconObjectInlineRight, IconObjectLeft, IconObjectRight, IconPreviousArrow, IconTable, IconTableCellProperties, IconTableColumn, IconTableLayout, IconTableMergeCell, IconTableProperties, IconTableRow } from "@ckeditor/ckeditor5-icons";
10
12
  import { BalloonPanelView, ButtonView, ColorSelectorView, ContextualBalloon, DropdownButtonView, FocusCycler, FormHeaderView, FormRowView, InputTextView, LabelView, LabeledFieldView, MenuBarMenuView, SplitButtonView, SwitchButtonView, ToolbarView, UIModel, View, ViewCollection, addKeyboardHandlingForGrid, addListToDropdown, clickOutsideHandler, createDropdown, createLabeledDropdown, createLabeledInputText, getLocalizedColorOptions, normalizeColorOptions, submitHandler } from "@ckeditor/ckeditor5-ui";
11
13
  import { ClipboardMarkersUtils, ClipboardPipeline } from "@ckeditor/ckeditor5-clipboard";
12
- import { DomEventObserver, Matcher, ModelElement, addBackgroundStylesRules, addBorderStylesRules, addMarginStylesRules, addPaddingStylesRules, enableViewPlaceholder, isColorStyleValue, isLengthStyleValue, isPercentageStyleValue } from "@ckeditor/ckeditor5-engine";
13
14
 
14
15
  /**
15
16
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
@@ -970,6 +971,20 @@ function isEntireCellsLineHeader({ table, row, column }) {
970
971
  function isTableCellTypeEnabled(editor) {
971
972
  return editor.model.schema.checkAttribute("tableCell", "tableCellType");
972
973
  }
974
+ /**
975
+ * Yields every empty block (typically a `paragraph`) found inside the given table's cells.
976
+ *
977
+ * @internal
978
+ */
979
+ function* getEmptyTableCellBlocks(table) {
980
+ for (const row of table.getChildren()) {
981
+ if (!row.is("element", "tableRow")) continue;
982
+ for (const cell of row.getChildren()) {
983
+ if (!cell.is("element", "tableCell")) continue;
984
+ for (const block of cell.getChildren()) if (block.is("element") && block.isEmpty) yield block;
985
+ }
986
+ }
987
+ }
973
988
 
974
989
  /**
975
990
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
@@ -1809,6 +1824,16 @@ function getElementWidthInPixels(domElement) {
1809
1824
  else return parseFloat(styles.width);
1810
1825
  }
1811
1826
  /**
1827
+ * Returns the inner pixel width of a given editing root, or `null` if the root has no
1828
+ * DOM element attached yet (e.g. it hasn't been rendered for the first time).
1829
+ *
1830
+ * @internal
1831
+ */
1832
+ function getEditableWidth(editor, rootName) {
1833
+ const domRoot = editor.editing.view.getDomRoot(rootName);
1834
+ return domRoot ? getElementWidthInPixels(domRoot) : null;
1835
+ }
1836
+ /**
1812
1837
  * Returns the column indexes on the left and right edges of a cell. They differ if the cell spans
1813
1838
  * across multiple columns.
1814
1839
  *
@@ -1881,6 +1906,9 @@ function sumArray(array) {
1881
1906
  * @returns An array of column widths guaranteed to sum up to 100%.
1882
1907
  */
1883
1908
  function normalizeColumnWidths(columnWidths) {
1909
+ const hasPixels = columnWidths.some((width) => typeof width === "string" && width.endsWith("px"));
1910
+ const hasPercentages = columnWidths.some((width) => typeof width === "string" && width.endsWith("%"));
1911
+ if (hasPixels && !hasPercentages) return columnWidths.map((width) => width === "auto" || width === void 0 ? "auto" : `${toPrecision(width)}px`);
1884
1912
  let normalizedWidths = calculateMissingColumnWidths(columnWidths.map((width) => {
1885
1913
  if (width === "auto" || width === void 0) return "auto";
1886
1914
  return parseFloat(width.replace("%", ""));
@@ -1979,6 +2007,26 @@ function getTableColumnsWidths(element) {
1979
2007
  return getTableColumnElements(element).map((column) => column.getAttribute("columnWidth"));
1980
2008
  }
1981
2009
  /**
2010
+ * Tells whether a table is in the pixel width mode, that is, its `tableWidth` attribute is expressed in pixels.
2011
+ * The `tableWidth` unit is the single source of truth for the whole table's width mode.
2012
+ *
2013
+ * @internal
2014
+ * @param table A 'table' model element.
2015
+ */
2016
+ function isTableWidthInPixels(table) {
2017
+ const tableWidth = table.getAttribute("tableWidth");
2018
+ return typeof tableWidth === "string" && tableWidth.trim().endsWith("px");
2019
+ }
2020
+ /**
2021
+ * Tells whether the given column widths are expressed in pixels.
2022
+ *
2023
+ * @internal
2024
+ * @param columnWidths An array of column widths.
2025
+ */
2026
+ function isColumnWidthsInPixels(columnWidths) {
2027
+ return columnWidths.some((width) => typeof width === "string" && width.endsWith("px"));
2028
+ }
2029
+ /**
1982
2030
  * Translates the `colSpan` model attribute into additional column widths and returns the resulting array.
1983
2031
  *
1984
2032
  * @internal
@@ -1999,6 +2047,19 @@ function translateColSpanAttribute(element, writer) {
1999
2047
  return acc;
2000
2048
  }, []);
2001
2049
  }
2050
+ /**
2051
+ * Removes the `tableCellWidth` attribute from every cell of the given table. Once a column is resized (with the resize
2052
+ * handler or the column-width command), a per-cell width is obsolete - the column width governs the layout - so it is
2053
+ * dropped to keep the model clean.
2054
+ *
2055
+ * @internal
2056
+ */
2057
+ function removeCellWidthsFromTable(writer, table) {
2058
+ for (const row of table.getChildren()) {
2059
+ if (!row.is("element", "tableRow")) continue;
2060
+ for (const cell of row.getChildren()) writer.removeAttribute("tableCellWidth", cell);
2061
+ }
2062
+ }
2002
2063
 
2003
2064
  /**
2004
2065
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
@@ -2388,10 +2449,12 @@ var TableUtils = class extends Plugin {
2388
2449
  else if (column === removedColumnIndex) writer.remove(cell);
2389
2450
  if (tableColumns[removedColumnIndex]) {
2390
2451
  const adjacentColumn = removedColumnIndex === 0 ? tableColumns[1] : tableColumns[removedColumnIndex - 1];
2391
- const removedColumnWidth = parseFloat(tableColumns[removedColumnIndex].getAttribute("columnWidth"));
2452
+ const removedColumnWidthAttribute = tableColumns[removedColumnIndex].getAttribute("columnWidth");
2453
+ const removedColumnWidth = parseFloat(removedColumnWidthAttribute);
2392
2454
  const adjacentColumnWidth = parseFloat(adjacentColumn.getAttribute("columnWidth"));
2393
2455
  writer.remove(tableColumns[removedColumnIndex]);
2394
- writer.setAttribute("columnWidth", removedColumnWidth + adjacentColumnWidth + "%", adjacentColumn);
2456
+ const unit = removedColumnWidthAttribute.trim().endsWith("px") ? "px" : "%";
2457
+ writer.setAttribute("columnWidth", `${removedColumnWidth + adjacentColumnWidth}${unit}`, adjacentColumn);
2395
2458
  }
2396
2459
  }
2397
2460
  if (!removeEmptyRows(table, this)) removeEmptyColumns(table, this);
@@ -3503,11 +3566,16 @@ var InsertTableCommand = class extends Command {
3503
3566
  * @param options.footerRows The number of footer rows. If not provided it will default to
3504
3567
  * {@link module:table/tableconfig~TableConfig#defaultFooters `config.table.defaultFooters`} table config.
3505
3568
  * This option is ignored when {@link module:table/tableconfig~TableConfig#enableFooters `config.table.enableFooters`} is `false`.
3569
+ * @param options.inheritTextFormattingAttributes Whether every empty cell should inherit the `copyOnEnter` text
3570
+ * formatting attributes (e.g. bold, font color) that were uniformly active in the content right before
3571
+ * the table, so that whichever cell the user starts typing in first continues that formatting. Defaults
3572
+ * to `true`.
3506
3573
  * @fires execute
3507
3574
  */
3508
3575
  execute(options = {}) {
3509
3576
  const editor = this.editor;
3510
3577
  const model = editor.model;
3578
+ const selection = model.document.selection;
3511
3579
  const tableUtils = editor.plugins.get("TableUtils");
3512
3580
  const areTableFootersEnabled = !!editor.config.get("table.enableFooters");
3513
3581
  const defaultRows = editor.config.get("table.defaultHeadings.rows");
@@ -3518,6 +3586,7 @@ var InsertTableCommand = class extends Command {
3518
3586
  if (areTableFootersEnabled && options.footerRows === void 0 && defaultFooterRows) options.footerRows = defaultFooterRows;
3519
3587
  if (!areTableFootersEnabled && "footerRows" in options) delete options.footerRows;
3520
3588
  model.change((writer) => {
3589
+ const selectionAttributesToCopy = Array.from(_getCopyOnEnterAttributes(model.schema, selection.getAttributes()));
3521
3590
  const table = tableUtils.createTable(writer, options);
3522
3591
  model.insertObject(table, null, null, { findOptimalPosition: "auto" });
3523
3592
  writer.setSelection(writer.createPositionAt(table.getNodeByPath([
@@ -3525,6 +3594,7 @@ var InsertTableCommand = class extends Command {
3525
3594
  0,
3526
3595
  0
3527
3596
  ]), 0));
3597
+ if (options.inheritTextFormattingAttributes !== false && selectionAttributesToCopy.length) for (const cellBlock of getEmptyTableCellBlocks(table)) for (const [key, value] of selectionAttributesToCopy) writer.setAttribute(ModelDocumentSelection._getStoreAttributeKey(key), value, cellBlock);
3528
3598
  });
3529
3599
  }
3530
3600
  };
@@ -3695,7 +3765,8 @@ var InsertColumnCommand = class extends Command {
3695
3765
  * The command is registered by {@link module:table/tableediting~TableEditing} as the `'splitTableCellVertically'`
3696
3766
  * and `'splitTableCellHorizontally'` editor commands.
3697
3767
  *
3698
- * You can split any cell vertically or horizontally by executing this command. For example, to split the selected table cell vertically:
3768
+ * You can split any cell vertically or horizontally by executing this command. When multiple cells are selected, each of them
3769
+ * is split, and the whole operation is a single undo step. For example, to split the selected table cells vertically:
3699
3770
  *
3700
3771
  * ```ts
3701
3772
  * editor.execute( 'splitTableCellVertically' );
@@ -3721,16 +3792,19 @@ var SplitCellCommand = class extends Command {
3721
3792
  */
3722
3793
  refresh() {
3723
3794
  const selectedCells = this.editor.plugins.get("TableUtils").getSelectionAffectedTableCells(this.editor.model.document.selection);
3724
- this.isEnabled = selectedCells.length === 1;
3795
+ this.isEnabled = selectedCells.length > 0;
3725
3796
  }
3726
3797
  /**
3727
3798
  * @inheritDoc
3728
3799
  */
3729
3800
  execute() {
3730
3801
  const tableUtils = this.editor.plugins.get("TableUtils");
3731
- const tableCell = tableUtils.getSelectionAffectedTableCells(this.editor.model.document.selection)[0];
3732
- if (this.direction === "horizontally") tableUtils.splitCellHorizontally(tableCell, 2);
3733
- else tableUtils.splitCellVertically(tableCell, 2);
3802
+ const tableCells = tableUtils.getSelectionAffectedTableCells(this.editor.model.document.selection);
3803
+ const isHorizontal = this.direction === "horizontally";
3804
+ this.editor.model.change(() => {
3805
+ for (const tableCell of tableCells) if (isHorizontal) tableUtils.splitCellHorizontally(tableCell, 2);
3806
+ else tableUtils.splitCellVertically(tableCell, 2);
3807
+ });
3734
3808
  }
3735
3809
  };
3736
3810
 
@@ -8552,7 +8626,8 @@ var TableCellPropertiesUI = class extends Plugin {
8552
8626
  const commands = this.editor.commands;
8553
8627
  const borderStyleCommand = commands.get("tableCellBorderStyle");
8554
8628
  Object.entries(propertyToCommandMap$1).flatMap(([property, commandName]) => {
8555
- const command = commands.get(commandName);
8629
+ const effectiveCommandName = property === "width" ? this._getWidthCommandName() : commandName;
8630
+ const command = commands.get(effectiveCommandName);
8556
8631
  if (!command) return [];
8557
8632
  const propertyKey = property;
8558
8633
  let defaultValue;
@@ -8652,7 +8727,8 @@ var TableCellPropertiesUI = class extends Plugin {
8652
8727
  setErrorTextDebounced.cancel();
8653
8728
  if (!this._isReady) return;
8654
8729
  if (validator(newValue)) {
8655
- this.editor.execute(commandName, {
8730
+ const executedCommandName = commandName === "tableCellWidth" ? this._getWidthCommandName() : commandName;
8731
+ this.editor.execute(executedCommandName, {
8656
8732
  value: newValue,
8657
8733
  batch: this._undoStepBatch
8658
8734
  });
@@ -8660,6 +8736,16 @@ var TableCellPropertiesUI = class extends Plugin {
8660
8736
  } else setErrorTextDebounced();
8661
8737
  };
8662
8738
  }
8739
+ /**
8740
+ * Returns the command that drives the width field. When the table is resized and the selection maps onto a single
8741
+ * column, the width field is bound to (and executes) the `'tableColumnWidth'` command, so the width applies to the
8742
+ * column instead of being set as a per-cell width that the column group would shadow. Otherwise it falls back to
8743
+ * the `'tableCellWidth'` command.
8744
+ */
8745
+ _getWidthCommandName() {
8746
+ const columnWidthCommand = this.editor.commands.get("tableColumnWidth");
8747
+ return columnWidthCommand && columnWidthCommand.isEnabled ? "tableColumnWidth" : "tableCellWidth";
8748
+ }
8663
8749
  };
8664
8750
 
8665
8751
  /**
@@ -9560,11 +9646,14 @@ function enableLegacyHorizontalAlignmentAttribute(conversion) {
9560
9646
  if (!modelElement?.is("element")) return;
9561
9647
  const alignValue = data.viewItem.getAttribute("align");
9562
9648
  if (!conversionApi.consumable.consume(data.viewItem, { attributes: ["align"] })) return;
9563
- for (const child of modelElement.getChildren()) if (child.is("element")) applyAlignmentToChild(child, alignValue, conversionApi);
9649
+ for (const child of modelElement.getChildren())
9650
+ /* v8 ignore else -- A table cell only contains block elements, so its children are always elements. */
9651
+ if (child.is("element")) applyAlignmentToChild(child, alignValue, conversionApi);
9564
9652
  }, { priority: "low" });
9565
9653
  });
9566
9654
  function applyAlignmentToChild(child, alignValue, { schema, writer }) {
9567
9655
  const definition = schema.getDefinition(child);
9656
+ /* v8 ignore else -- A converted child element is always registered in the schema, so it always has a definition. */
9568
9657
  if (definition) for (const attrName of definition.allowAttributes) {
9569
9658
  if (child.hasAttribute(attrName)) continue;
9570
9659
  const { blockAlignment } = schema.getAttributeProperties(attrName);
@@ -9704,12 +9793,14 @@ function enableCellTypeProperty(editor) {
9704
9793
  const cell = change.range.start.nodeAfter;
9705
9794
  if (cell?.is("element", "tableCell") && cell.root.rootName !== "$graveyard") {
9706
9795
  const table = cell.findAncestor("table");
9796
+ /* v8 ignore else -- A table cell always lives inside a table, so it always has a table ancestor here. */
9707
9797
  if (table) tablesToCheck.add(table);
9708
9798
  }
9709
9799
  }
9710
9800
  if (change.type === "insert" && change.position.nodeAfter) {
9711
9801
  for (const { item } of model.createRangeOn(change.position.nodeAfter)) if (item.is("element", "tableCell") && item.getAttribute("tableCellType") && item.root.rootName !== "$graveyard") {
9712
9802
  const table = item.findAncestor("table");
9803
+ /* v8 ignore else -- An inserted table cell always lives inside a table, so it always has a table ancestor here. */
9713
9804
  if (table) tablesToCheck.add(table);
9714
9805
  }
9715
9806
  }
@@ -9880,7 +9971,8 @@ var TableLayoutUI = class extends Plugin {
9880
9971
  if (!editor.plugins.has("TablePropertiesUI")) return;
9881
9972
  const tablePropertiesUI = plugins.get("TablePropertiesUI");
9882
9973
  ui.componentFactory.add("tableProperties", (locale) => {
9883
- return createTableTypeDropdown(editor, new SplitButtonView(locale, tablePropertiesUI._createTablePropertiesButton()));
9974
+ const splitButtonView = new SplitButtonView(locale, tablePropertiesUI._createTablePropertiesButton());
9975
+ return createTableTypeDropdown(editor, splitButtonView);
9884
9976
  });
9885
9977
  }
9886
9978
  };
@@ -9980,13 +10072,19 @@ var InsertTableLayoutCommand = class extends Command {
9980
10072
  *
9981
10073
  * @param options.rows The number of rows to create in the inserted table. Default value is 2.
9982
10074
  * @param options.columns The number of columns to create in the inserted table. Default value is 2.
10075
+ * @param options.inheritTextFormattingAttributes Whether every empty cell should inherit the `copyOnEnter` text
10076
+ * formatting attributes (e.g. bold, font color) that were uniformly active in the content right before
10077
+ * the table, so that whichever cell the user starts typing in first continues that formatting. Defaults
10078
+ * to `true`.
9983
10079
  * @fires execute
9984
10080
  */
9985
10081
  execute(options = {}) {
9986
10082
  const editor = this.editor;
9987
10083
  const model = editor.model;
10084
+ const selection = model.document.selection;
9988
10085
  const tableUtils = editor.plugins.get("TableUtils");
9989
10086
  model.change((writer) => {
10087
+ const selectionAttributesToCopy = Array.from(_getCopyOnEnterAttributes(model.schema, selection.getAttributes()));
9990
10088
  const normalizedOptions = {
9991
10089
  rows: options.rows || 2,
9992
10090
  columns: options.columns || 2
@@ -10006,6 +10104,7 @@ var InsertTableLayoutCommand = class extends Command {
10006
10104
  0,
10007
10105
  0
10008
10106
  ]), 0));
10107
+ if (options.inheritTextFormattingAttributes !== false && selectionAttributesToCopy.length) for (const cellBlock of getEmptyTableCellBlocks(table)) for (const [key, value] of selectionAttributesToCopy) writer.setAttribute(ModelDocumentSelection._getStoreAttributeKey(key), value, cellBlock);
10009
10108
  });
10010
10109
  }
10011
10110
  };
@@ -10047,6 +10146,7 @@ var TableWidthsCommand = class extends Command {
10047
10146
  if (!columnWidths && !tableColumnGroup) return;
10048
10147
  if (!columnWidths) return writer.remove(tableColumnGroup);
10049
10148
  const widths = normalizeColumnWidths(columnWidths);
10149
+ removeCellWidthsFromTable(writer, table);
10050
10150
  if (!tableColumnGroup) {
10051
10151
  const colGroupElement = writer.createElement("tableColumnGroup");
10052
10152
  widths.forEach((columnWidth) => writer.appendElement("tableColumn", { columnWidth }, colGroupElement));
@@ -10056,6 +10156,76 @@ var TableWidthsCommand = class extends Command {
10056
10156
  }
10057
10157
  };
10058
10158
 
10159
+ /**
10160
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
10161
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
10162
+ */
10163
+ /**
10164
+ * @module table/tablecolumnresize/commands/tablecolumnwidthcommand
10165
+ */
10166
+ /**
10167
+ * The table column width command.
10168
+ *
10169
+ * The command is registered by the {@link module:table/tablecolumnresize/tablecolumnresizeediting~TableColumnResizeEditing}
10170
+ * as the `'tableColumnWidth'` editor command.
10171
+ *
10172
+ * It sets the width of every column covered by the selected cells (keeping the whole table's width mode consistent),
10173
+ * so the change actually takes effect in a resized table - where a per-cell width would be shadowed by the column
10174
+ * group. The command is enabled whenever the selection maps onto the columns of a resized, regular table.
10175
+ *
10176
+ * ```ts
10177
+ * editor.execute( 'tableColumnWidth', {
10178
+ * value: '150px'
10179
+ * } );
10180
+ * ```
10181
+ *
10182
+ * **Note**: This command adds a default `'px'` unit to numeric values. Executing:
10183
+ *
10184
+ * ```ts
10185
+ * editor.execute( 'tableColumnWidth', {
10186
+ * value: '150'
10187
+ * } );
10188
+ * ```
10189
+ *
10190
+ * will set the column width to `'150px'`.
10191
+ */
10192
+ var TableColumnWidthCommand = class extends Command {
10193
+ /**
10194
+ * @inheritDoc
10195
+ */
10196
+ refresh() {
10197
+ const editor = this.editor;
10198
+ const tableUtils = editor.plugins.get("TableUtils");
10199
+ const tableColumnResize = editor.plugins.get("TableColumnResizeEditing");
10200
+ const tableCells = tableUtils.getSelectionAffectedTableCells(editor.model.document.selection);
10201
+ const columns = tableColumnResize.getColumnIndexesForCells(tableCells);
10202
+ if (!columns) {
10203
+ this.isEnabled = false;
10204
+ this.value = null;
10205
+ return;
10206
+ }
10207
+ this.isEnabled = true;
10208
+ this.value = tableColumnResize.getTableColumnElements(columns.table)[columns.columnIndexes[0]].getAttribute("columnWidth");
10209
+ }
10210
+ /**
10211
+ * @inheritDoc
10212
+ */
10213
+ execute(options = {}) {
10214
+ const editor = this.editor;
10215
+ const model = editor.model;
10216
+ const tableUtils = editor.plugins.get("TableUtils");
10217
+ const tableColumnResize = editor.plugins.get("TableColumnResizeEditing");
10218
+ const value = addDefaultUnitToNumericValue(options.value, "px");
10219
+ const tableCells = tableUtils.getSelectionAffectedTableCells(model.document.selection);
10220
+ const columns = tableColumnResize.getColumnIndexesForCells(tableCells);
10221
+ if (!value || !columns || Number.isNaN(parseFloat(value))) return;
10222
+ model.enqueueChange(options.batch, (writer) => {
10223
+ tableColumnResize.applyColumnWidths(writer, columns.table, columns.columnIndexes, value);
10224
+ removeCellWidthsFromTable(writer, columns.table);
10225
+ });
10226
+ }
10227
+ };
10228
+
10059
10229
  /**
10060
10230
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
10061
10231
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
@@ -10118,10 +10288,6 @@ const toPx = /* #__PURE__ */ toUnit("px");
10118
10288
  * The table column resize editing plugin.
10119
10289
  */
10120
10290
  var TableColumnResizeEditing = class extends Plugin {
10121
- /**
10122
- * A flag indicating if the column resizing is in progress.
10123
- */
10124
- _isResizingActive;
10125
10291
  /**
10126
10292
  * A temporary storage for the required data needed to correctly calculate the widths of the resized columns. This storage is
10127
10293
  * initialized when column resizing begins, and is purged upon completion.
@@ -10175,7 +10341,7 @@ var TableColumnResizeEditing = class extends Plugin {
10175
10341
  */
10176
10342
  constructor(editor) {
10177
10343
  super(editor);
10178
- this._isResizingActive = false;
10344
+ this.set("_isResizingActive", false);
10179
10345
  this.set("_isResizingAllowed", true);
10180
10346
  this._resizingData = null;
10181
10347
  this._domEmitter = new (DomEmitterMixin())();
@@ -10186,6 +10352,10 @@ var TableColumnResizeEditing = class extends Plugin {
10186
10352
  for (const root of editor.editing.view.document.roots) writer[classAction]("ck-column-resize_disabled", editor.editing.view.document.getRoot(root.rootName));
10187
10353
  });
10188
10354
  });
10355
+ this.on("change:_isResizingActive", (evt, name, value) => {
10356
+ const classAction = value ? "add" : "remove";
10357
+ global.document.body.classList[classAction]("ck-table-column-resize__resizing-cursor");
10358
+ });
10189
10359
  }
10190
10360
  /**
10191
10361
  * @inheritDoc
@@ -10196,6 +10366,8 @@ var TableColumnResizeEditing = class extends Plugin {
10196
10366
  this._registerConverters();
10197
10367
  this._registerResizingListeners();
10198
10368
  this._registerResizerInserter();
10369
+ this.decorate("_setResizingTableWidth");
10370
+ this.decorate("_getResizingTableWidth");
10199
10371
  const editor = this.editor;
10200
10372
  const columnResizePlugin = editor.plugins.get("TableColumnResize");
10201
10373
  editor.plugins.get("TableEditing").registerAdditionalSlot({
@@ -10205,16 +10377,43 @@ var TableColumnResizeEditing = class extends Plugin {
10205
10377
  const tableWidthsCommand = new TableWidthsCommand(editor);
10206
10378
  editor.commands.add("resizeTableWidth", tableWidthsCommand);
10207
10379
  editor.commands.add("resizeColumnWidths", tableWidthsCommand);
10380
+ editor.commands.add("tableColumnWidth", new TableColumnWidthCommand(editor));
10208
10381
  this.bind("_isResizingAllowed").to(editor, "isReadOnly", columnResizePlugin, "isEnabled", tableWidthsCommand, "isEnabled", (isEditorReadOnly, isPluginEnabled, isTableWidthsCommandCommandEnabled) => !isEditorReadOnly && isPluginEnabled && isTableWidthsCommandCommandEnabled);
10209
10382
  }
10210
10383
  /**
10211
10384
  * @inheritDoc
10212
10385
  */
10386
+ afterInit() {
10387
+ const editor = this.editor;
10388
+ const tableWidthCommand = editor.commands.get("tableWidth");
10389
+ if (tableWidthCommand) {
10390
+ this.listenTo(tableWidthCommand, "execute", (evt, args) => {
10391
+ const options = args[0] || (args[0] = {});
10392
+ if (!options.batch) options.batch = editor.model.createBatch();
10393
+ }, { priority: "high" });
10394
+ this.listenTo(tableWidthCommand, "execute", (evt, args) => {
10395
+ const options = args[0];
10396
+ const table = getSelectionAffectedTable(editor.model.document.selection);
10397
+ editor.model.enqueueChange(options.batch, (writer) => this._reconcileColumnUnits(writer, table));
10398
+ }, { priority: "low" });
10399
+ }
10400
+ }
10401
+ /**
10402
+ * @inheritDoc
10403
+ */
10213
10404
  destroy() {
10214
10405
  this._domEmitter.stopListening();
10406
+ this._isResizingActive = false;
10215
10407
  super.destroy();
10216
10408
  }
10217
10409
  /**
10410
+ * The table for which a column resize is currently in progress, or `null` if no resize is active.
10411
+ * Only one table can be resized at a time.
10412
+ */
10413
+ get resizingTable() {
10414
+ return this._resizingData ? this._resizingData.elements.modelTable : null;
10415
+ }
10416
+ /**
10218
10417
  * Returns a 'tableColumnGroup' element from the 'table'.
10219
10418
  *
10220
10419
  * @param element A 'table' or 'tableColumnGroup' element.
@@ -10242,6 +10441,144 @@ var TableColumnResizeEditing = class extends Plugin {
10242
10441
  return getTableColumnsWidths(element);
10243
10442
  }
10244
10443
  /**
10444
+ * Returns the table and the sorted, unique indexes of the columns covered by the given cells (a `colspan` cell
10445
+ * covers several columns). Returns `null` when the selection cannot be mapped onto columns - a non-resized table
10446
+ * or an irregular column structure.
10447
+ *
10448
+ * @param cells An array of 'tableCell' model elements.
10449
+ */
10450
+ getColumnIndexesForCells(cells) {
10451
+ const table = cells.length ? cells[0].findAncestor("table") : null;
10452
+ if (!table) return null;
10453
+ const columns = getTableColumnElements(table);
10454
+ if (!columns.length || columns.length !== this._tableUtilsPlugin.getColumns(table) || columns.some((column) => column.hasAttribute("colSpan"))) return null;
10455
+ const columnIndexes = /* @__PURE__ */ new Set();
10456
+ for (const cell of cells) {
10457
+ const { leftEdge, rightEdge } = getColumnEdgesIndexes(cell, this._tableUtilsPlugin);
10458
+ for (let index = leftEdge; index <= rightEdge; index++) columnIndexes.add(index);
10459
+ }
10460
+ return {
10461
+ table,
10462
+ columnIndexes: Array.from(columnIndexes).sort((indexA, indexB) => indexA - indexB)
10463
+ };
10464
+ }
10465
+ /**
10466
+ * Applies the given width to every column in `columnIndexes`, keeping the whole table's width mode consistent
10467
+ * (see {@link module:table/tablecolumnresize/utils~isTableWidthInPixels}).
10468
+ *
10469
+ * @param writer A model writer instance.
10470
+ * @param table A 'table' model element.
10471
+ * @param columnIndexes Indexes of the columns the width is applied to.
10472
+ * @param value The width to apply. May be expressed in pixels or as a percentage.
10473
+ */
10474
+ applyColumnWidths(writer, table, columnIndexes, value) {
10475
+ if (isTableWidthInPixels(table)) applyPixelColumnWidths(writer, table, columnIndexes, value);
10476
+ else this._applyPercentageColumnWidths(writer, table, columnIndexes, value);
10477
+ }
10478
+ /**
10479
+ * Converts the column widths of a resized table to the unit of the table's own width (`px` or `%`), so a table
10480
+ * width change (in the table properties) also switches the columns' unit. It is a no-op when the table is not
10481
+ * resized or the columns already use that unit.
10482
+ */
10483
+ _reconcileColumnUnits(writer, table) {
10484
+ const tableColumnGroup = getColumnGroupElement(table);
10485
+ if (!tableColumnGroup) return;
10486
+ const columnWidths = getTableColumnsWidths(tableColumnGroup);
10487
+ const tableIsPixels = isTableWidthInPixels(table);
10488
+ if (tableIsPixels === isColumnWidthsInPixels(columnWidths)) return;
10489
+ let reconciledWidths;
10490
+ if (tableIsPixels) {
10491
+ const tableWidthInPixels = parseFloat(table.getAttribute("tableWidth"));
10492
+ reconciledWidths = columnWidths.map((width) => `${toPrecision(parseFloat(width) / 100 * tableWidthInPixels)}px`);
10493
+ } else {
10494
+ const totalWidth = sumArray(columnWidths.map((width) => parseFloat(width)));
10495
+ reconciledWidths = columnWidths.map((width) => `${toPrecision(parseFloat(width) / totalWidth * 100)}%`);
10496
+ }
10497
+ updateColumnElements(getTableColumnElements(tableColumnGroup), tableColumnGroup, reconciledWidths, writer);
10498
+ }
10499
+ /**
10500
+ * Applies a column width in the percentage mode: the target column gets the (clamped) percentage and the remaining
10501
+ * columns are redistributed proportionally so that all the widths keep summing up to 100%.
10502
+ */
10503
+ _applyPercentageColumnWidths(writer, table, columnIndexes, value) {
10504
+ const columns = getTableColumnElements(table);
10505
+ const widths = getTableColumnsWidths(table).map((width) => parseFloat(width));
10506
+ if (columnIndexes.includes(columns.length - 1)) {
10507
+ this._growTableToColumnWidths(writer, table, columns, widths, columnIndexes, value);
10508
+ return;
10509
+ }
10510
+ const targetPercentage = value.trim().endsWith("%") ? parseFloat(value) : parseFloat(value) / getTableWidthInPixels(table, this.editor) * 100;
10511
+ const nextWidths = widths.slice();
10512
+ for (const band of getContiguousBands(columnIndexes)) {
10513
+ const neighbor = band.end + 1;
10514
+ const bandCount = band.indexes.length;
10515
+ const available = band.indexes.reduce((sum, index) => sum + widths[index], widths[neighbor]);
10516
+ if (available < 5 * (bandCount + 1)) {
10517
+ const equalWidth = available / (bandCount + 1);
10518
+ for (const index of band.indexes) nextWidths[index] = equalWidth;
10519
+ nextWidths[neighbor] = equalWidth;
10520
+ continue;
10521
+ }
10522
+ const target = clamp(targetPercentage, 5, (available - 5) / bandCount);
10523
+ for (const index of band.indexes) nextWidths[index] = target;
10524
+ nextWidths[neighbor] = available - target * bandCount;
10525
+ }
10526
+ columns.forEach((columnElement, index) => {
10527
+ writer.setAttribute("columnWidth", `${toPrecision(nextWidths[index])}%`, columnElement);
10528
+ });
10529
+ }
10530
+ /**
10531
+ * Applies a width to the target columns in the percentage mode when the selection reaches the last column. As
10532
+ * there is no next column to balance against, the table itself grows or shrinks (like dragging the last column's
10533
+ * right edge): every other column keeps its absolute width, so expressed against the resized table their
10534
+ * percentages scale, and the table's own width scales by the inverse.
10535
+ */
10536
+ _growTableToColumnWidths(writer, table, columns, widths, columnIndexes, value) {
10537
+ const targetColumns = new Set(columnIndexes);
10538
+ const targetCount = columnIndexes.length;
10539
+ const otherColumnsShare = 100 - columnIndexes.reduce((sum, index) => sum + widths[index], 0);
10540
+ if (otherColumnsShare <= 0) {
10541
+ const equalWidth = 100 / columns.length;
10542
+ columns.forEach((columnElement) => {
10543
+ writer.setAttribute("columnWidth", `${toPrecision(equalWidth)}%`, columnElement);
10544
+ });
10545
+ return;
10546
+ }
10547
+ let targetPercentage;
10548
+ if (value.trim().endsWith("%")) targetPercentage = parseFloat(value);
10549
+ else {
10550
+ const pixelWidth = parseFloat(value);
10551
+ targetPercentage = pixelWidth / (otherColumnsShare / 100 * getTableWidthInPixels(table, this.editor) + targetCount * pixelWidth) * 100;
10552
+ }
10553
+ const target = clamp(targetPercentage, 5, Math.max(5, (100 - 5 * (columns.length - targetCount)) / targetCount));
10554
+ const scale = (100 - target * targetCount) / otherColumnsShare;
10555
+ columns.forEach((columnElement, index) => {
10556
+ const next = targetColumns.has(index) ? target : widths[index] * scale;
10557
+ writer.setAttribute("columnWidth", `${toPrecision(next)}%`, columnElement);
10558
+ });
10559
+ const tableWidth = parseFloat(table.getAttribute("tableWidth"));
10560
+ if (!Number.isNaN(tableWidth)) writer.setAttribute("tableWidth", `${toPrecision(tableWidth / scale)}%`, table);
10561
+ }
10562
+ /**
10563
+ * Applies `width` to whichever element currently represents the table's actual width - by default the
10564
+ * widget's `<figure>`. Passing `null` clears it instead of setting anything.
10565
+ *
10566
+ * @internal
10567
+ */
10568
+ _setResizingTableWidth(writer, viewFigure, width) {
10569
+ if (width === null) writer.removeStyle("width", viewFigure);
10570
+ else writer.setStyle("width", width, viewFigure);
10571
+ }
10572
+ /**
10573
+ * Returns the table's current actual width, read from whichever element holds it - by default the
10574
+ * widget's `<figure>`.
10575
+ *
10576
+ * @internal
10577
+ */
10578
+ _getResizingTableWidth(viewFigure) {
10579
+ return viewFigure.getStyle("width");
10580
+ }
10581
+ /**
10245
10582
  * Registers new attributes for a table model element.
10246
10583
  */
10247
10584
  _extendSchema() {
@@ -10273,8 +10610,11 @@ var TableColumnResizeEditing = class extends Plugin {
10273
10610
  const tableColumnGroup = this.getColumnGroupElement(table);
10274
10611
  const columns = this.getTableColumnElements(tableColumnGroup);
10275
10612
  const columnWidths = this.getTableColumnsWidths(tableColumnGroup);
10276
- let normalizedWidths = normalizeColumnWidths(columnWidths);
10613
+ const isPixelMode = isColumnWidthsInPixels(columnWidths);
10614
+ let normalizedWidths = isPixelMode ? normalizePixelColumnWidths(columnWidths, table) : normalizeColumnWidths(columnWidths);
10277
10615
  normalizedWidths = adjustColumnWidths(normalizedWidths, table, this);
10616
+ if (isPixelMode && normalizedWidths.length !== columnWidths.length) writer.setAttribute("tableWidth", `${toPrecision(sumArray(normalizedWidths))}px`, table);
10617
+ else normalizedWidths = scalePixelColumnsToTableWidth(normalizedWidths, table);
10278
10618
  if (isEqual(columnWidths, normalizedWidths)) continue;
10279
10619
  updateColumnElements(columns, tableColumnGroup, normalizedWidths, writer);
10280
10620
  changed = true;
@@ -10290,7 +10630,8 @@ var TableColumnResizeEditing = class extends Plugin {
10290
10630
  function adjustColumnWidths(columnWidths, table, plugin) {
10291
10631
  const newTableColumnsCount = plugin._tableUtilsPlugin.getColumns(table);
10292
10632
  if (newTableColumnsCount - columnWidths.length === 0) return columnWidths;
10293
- const widths = columnWidths.map((width) => Number(width.replace("%", "")));
10633
+ const isPixelMode = isColumnWidthsInPixels(columnWidths);
10634
+ const widths = columnWidths.map((width) => parseFloat(width));
10294
10635
  const cellSet = getAffectedCells(plugin.editor.model.document.differ, table);
10295
10636
  for (const cell of cellSet) {
10296
10637
  const currentColumnsDelta = newTableColumnsCount - widths.length;
@@ -10298,14 +10639,14 @@ var TableColumnResizeEditing = class extends Plugin {
10298
10639
  const hasMoreColumns = currentColumnsDelta > 0;
10299
10640
  const currentColumnIndex = plugin._tableUtilsPlugin.getCellLocation(cell).column;
10300
10641
  if (hasMoreColumns) {
10301
- const columnWidthsToInsert = createFilledArray(currentColumnsDelta, getColumnMinWidthAsPercentage(table, plugin.editor));
10642
+ const columnWidthsToInsert = createFilledArray(currentColumnsDelta, isPixelMode ? 40 : getColumnMinWidthAsPercentage(table, plugin.editor));
10302
10643
  widths.splice(currentColumnIndex, 0, ...columnWidthsToInsert);
10303
10644
  } else {
10304
10645
  const removedColumnWidths = widths.splice(currentColumnIndex, Math.abs(currentColumnsDelta));
10305
10646
  widths[currentColumnIndex] += sumArray(removedColumnWidths);
10306
10647
  }
10307
10648
  }
10308
- return widths.map((width) => width + "%");
10649
+ return widths.map((width) => `${width}${isPixelMode ? "px" : "%"}`);
10309
10650
  }
10310
10651
  /**
10311
10652
  * Returns a set of cells that have been changed in a given table.
@@ -10369,7 +10710,7 @@ var TableColumnResizeEditing = class extends Plugin {
10369
10710
  key: "columnWidth",
10370
10711
  value: (viewElement) => {
10371
10712
  const viewColWidth = viewElement.getStyle("width");
10372
- if (!viewColWidth || !viewColWidth.endsWith("%") && !viewColWidth.endsWith("pt")) return "auto";
10713
+ if (!viewColWidth || !viewColWidth.endsWith("%") && !viewColWidth.endsWith("pt") && !(viewColWidth.endsWith("px") && isViewTableWidthInPixels(viewElement))) return "auto";
10373
10714
  return viewColWidth;
10374
10715
  }
10375
10716
  }
@@ -10490,13 +10831,18 @@ var TableColumnResizeEditing = class extends Plugin {
10490
10831
  const { target } = domEventData;
10491
10832
  const modelTable = this.editor.editing.mapper.toModelElement(target.findAncestor("figure"));
10492
10833
  const viewTable = target.findAncestor("table");
10834
+ const viewFigure = target.findAncestor("figure");
10835
+ const isPixelMode = isTableWidthInPixels(modelTable);
10493
10836
  const columnWidthsInPx = _calculateDomColumnWidths(modelTable, this._tableUtilsPlugin, this.editor);
10494
10837
  if (!Array.from(viewTable.getChildren()).find((viewCol) => viewCol.is("element", "colgroup"))) this.editor.editing.view.change((viewWriter) => {
10495
- _insertColgroupElement(viewWriter, columnWidthsInPx, viewTable);
10838
+ _insertColgroupElement(viewWriter, columnWidthsInPx, viewTable, isPixelMode);
10496
10839
  });
10497
10840
  this._isResizingActive = true;
10498
10841
  this._resizingData = this._getResizingData(domEventData, columnWidthsInPx);
10499
- this.editor.editing.view.change((writer) => _applyResizingAttributesToTable(writer, viewTable, this._resizingData));
10842
+ this.editor.editing.view.change((writer) => {
10843
+ const initialWidth = _applyResizingAttributesToTable(writer, viewTable, this._resizingData);
10844
+ this._setResizingTableWidth(writer, viewFigure, initialWidth);
10845
+ });
10500
10846
  /**
10501
10847
  * Calculates the DOM columns' widths. It is done by taking the width of the widest cell
10502
10848
  * from each table column (we rely on the {@link module:table/tablewalker~TableWalker}
@@ -10524,28 +10870,29 @@ var TableColumnResizeEditing = class extends Plugin {
10524
10870
  * @param columnWidthsInPx Column widths.
10525
10871
  * @param viewTable A table view element.
10526
10872
  */
10527
- function _insertColgroupElement(viewWriter, columnWidthsInPx, viewTable) {
10873
+ function _insertColgroupElement(viewWriter, columnWidthsInPx, viewTable, isPixelMode) {
10528
10874
  const colgroup = viewWriter.createContainerElement("colgroup");
10529
10875
  for (let i = 0; i < columnWidthsInPx.length; i++) {
10530
10876
  const viewColElement = viewWriter.createEmptyElement("col");
10531
- const columnWidthInPc = `${toPrecision(columnWidthsInPx[i] / sumArray(columnWidthsInPx) * 100)}%`;
10532
- viewWriter.setStyle("width", columnWidthInPc, viewColElement);
10877
+ const columnWidth = isPixelMode ? `${toPrecision(columnWidthsInPx[i])}px` : `${toPrecision(columnWidthsInPx[i] / sumArray(columnWidthsInPx) * 100)}%`;
10878
+ viewWriter.setStyle("width", columnWidth, viewColElement);
10533
10879
  viewWriter.insert(viewWriter.createPositionAt(colgroup, "end"), viewColElement);
10534
10880
  }
10535
10881
  viewWriter.insert(viewWriter.createPositionAt(viewTable, 0), colgroup);
10536
10882
  }
10537
10883
  /**
10538
- * Applies the style and classes to the view table as the resizing begun.
10884
+ * Applies the classes to the view table as the resizing begun, and computes the initial live width.
10539
10885
  *
10540
10886
  * @param viewWriter A writer instance.
10541
10887
  * @param viewTable A table containing the clicked resizer.
10542
10888
  * @param resizingData Data related to the resizing.
10889
+ * @returns The table's current width as a `%` string, e.g. for seeding {@link #_setResizingTableWidth}.
10543
10890
  */
10544
10891
  function _applyResizingAttributesToTable(viewWriter, viewTable, resizingData) {
10545
- const figureInitialPcWidth = resizingData.widths.viewFigureWidth / resizingData.widths.viewFigureParentWidth;
10546
10892
  viewWriter.addClass("ck-table-resized", viewTable);
10547
10893
  viewWriter.addClass("ck-table-column-resizer__active", resizingData.elements.viewResizer);
10548
- viewWriter.setStyle("width", `${toPrecision(figureInitialPcWidth * 100)}%`, viewTable.findAncestor("figure"));
10894
+ const figureWidth = Math.max(resizingData.widths.tableWidth, resizingData.widths.viewFigureWidth);
10895
+ return resizingData.flags.isPixelMode ? `${toPrecision(figureWidth)}px` : `${toPrecision(figureWidth / resizingData.widths.viewFigureParentWidth * 100)}%`;
10549
10896
  }
10550
10897
  }
10551
10898
  /**
@@ -10570,22 +10917,40 @@ var TableColumnResizeEditing = class extends Plugin {
10570
10917
  this._onMouseUpHandler();
10571
10918
  return;
10572
10919
  }
10573
- const { columnPosition, flags: { isRightEdge, isTableCentered, isLtrContent }, elements: { viewFigure, viewLeftColumn, viewRightColumn, viewResizer }, widths: { viewFigureParentWidth, tableWidth, leftColumnWidth, rightColumnWidth } } = this._resizingData;
10920
+ const { plugins } = this.editor;
10921
+ const { columnPosition, flags: { isRightEdge, isTableCentered, isLtrContent, isPixelMode, isTableWidthWithinContainerAtDragStart, isTableScrollAllowed }, elements: { modelTable, viewFigure, viewLeftColumn, viewRightColumn, viewResizer }, widths: { viewFigureParentWidth, tableWidth, leftColumnWidth, rightColumnWidth } } = this._resizingData;
10574
10922
  const dxLowerBound = -leftColumnWidth + 40;
10575
- const dxUpperBound = isRightEdge ? viewFigureParentWidth - tableWidth : rightColumnWidth - 40;
10576
- const multiplier = (isLtrContent ? 1 : -1) * (isRightEdge && isTableCentered ? 2 : 1);
10577
- const dx = clamp((mouseEventData.clientX - columnPosition) * multiplier, Math.min(dxLowerBound, 0), Math.max(dxUpperBound, 0));
10923
+ const isTableScrollActive = !!(plugins.has("TableScrollEditing") ? plugins.get("TableScrollEditing") : null) && isTableScrollAllowed;
10924
+ const containerWidth = getEditableWidth(this.editor, modelTable.root.rootName);
10925
+ let dxUpperBound;
10926
+ if (isRightEdge) dxUpperBound = isTableScrollActive ? Infinity : viewFigureParentWidth - tableWidth;
10927
+ else dxUpperBound = rightColumnWidth - 40;
10928
+ const rawDx = mouseEventData.clientX - columnPosition;
10929
+ const ltrSign = isLtrContent ? 1 : -1;
10930
+ const isCenteredRightEdge = isRightEdge && isTableCentered;
10931
+ let dx;
10932
+ if (isTableScrollActive && isCenteredRightEdge) {
10933
+ const mouseDelta = rawDx * ltrSign;
10934
+ let newTableWidth;
10935
+ if (isTableWidthWithinContainerAtDragStart) {
10936
+ const crossoverPoint = (containerWidth - tableWidth) / 2;
10937
+ newTableWidth = mouseDelta <= crossoverPoint ? tableWidth + 2 * mouseDelta : containerWidth + (mouseDelta - crossoverPoint);
10938
+ } else {
10939
+ const crossoverPoint = containerWidth - tableWidth;
10940
+ newTableWidth = mouseDelta >= crossoverPoint ? tableWidth + mouseDelta : containerWidth + 2 * (mouseDelta - crossoverPoint);
10941
+ }
10942
+ dx = newTableWidth - tableWidth;
10943
+ } else dx = rawDx * (ltrSign * (isCenteredRightEdge ? 2 : 1));
10944
+ dx = clamp(dx, Math.min(dxLowerBound, 0), Math.max(dxUpperBound, 0));
10945
+ if (isTableScrollActive && isRightEdge) dx = clamp(applyContainerWidthResistance(tableWidth + dx, containerWidth) - tableWidth, Math.min(dxLowerBound, 0), Math.max(dxUpperBound, 0));
10578
10946
  if (dx === 0) return;
10579
10947
  this.editor.editing.view.change((writer) => {
10580
- const leftColumnWidthAsPercentage = toPrecision((leftColumnWidth + dx) * 100 / tableWidth);
10581
- writer.setStyle("width", `${leftColumnWidthAsPercentage}%`, viewLeftColumn);
10948
+ const toWidthValue = (widthInPx, basisInPx) => isPixelMode ? `${toPrecision(widthInPx)}px` : `${toPrecision(widthInPx * 100 / basisInPx)}%`;
10949
+ writer.setStyle("width", toWidthValue(leftColumnWidth + dx, tableWidth), viewLeftColumn);
10582
10950
  if (isRightEdge) {
10583
- const tableWidthAsPercentage = toPrecision((tableWidth + dx) * 100 / viewFigureParentWidth);
10584
- writer.setStyle("width", `${tableWidthAsPercentage}%`, viewFigure);
10585
- } else {
10586
- const rightColumnWidthAsPercentage = toPrecision((rightColumnWidth - dx) * 100 / tableWidth);
10587
- writer.setStyle("width", `${rightColumnWidthAsPercentage}%`, viewRightColumn);
10588
- }
10951
+ const tableFigureWidth = isPixelMode ? `${toPrecision(tableWidth + dx)}px` : `${toPrecision((tableWidth + dx) * 100 / viewFigureParentWidth)}%`;
10952
+ this._setResizingTableWidth(writer, viewFigure, tableFigureWidth);
10953
+ } else writer.setStyle("width", toWidthValue(rightColumnWidth - dx, tableWidth), viewRightColumn);
10589
10954
  });
10590
10955
  this._recalculateResizerElement(viewResizer);
10591
10956
  }
@@ -10598,7 +10963,8 @@ var TableColumnResizeEditing = class extends Plugin {
10598
10963
  _onMouseUpHandler() {
10599
10964
  this._initialMouseEventData = null;
10600
10965
  if (!this._isResizingActive) return;
10601
- const { viewResizer, modelTable, viewFigure, viewColgroup } = this._resizingData.elements;
10966
+ const { viewResizer, modelTable, viewFigure, viewTable, viewColgroup } = this._resizingData.elements;
10967
+ const { isPixelMode } = this._resizingData.flags;
10602
10968
  const editor = this.editor;
10603
10969
  const editingView = editor.editing.view;
10604
10970
  const tableColumnGroup = this.getColumnGroupElement(modelTable);
@@ -10607,19 +10973,22 @@ var TableColumnResizeEditing = class extends Plugin {
10607
10973
  const columnWidthsAttributeNew = viewColumns.map((column) => column.getStyle("width"));
10608
10974
  const isColumnWidthsAttributeChanged = !isEqual(columnWidthsAttributeOld, columnWidthsAttributeNew);
10609
10975
  const tableWidthAttributeOld = modelTable.getAttribute("tableWidth");
10610
- const tableWidthAttributeNew = viewFigure.getStyle("width");
10976
+ const tableWidthAttributeNew = this._getResizingTableWidth(viewFigure);
10611
10977
  const isTableWidthAttributeChanged = tableWidthAttributeOld !== tableWidthAttributeNew;
10612
- if (isColumnWidthsAttributeChanged || isTableWidthAttributeChanged) if (this._isResizingAllowed) editor.execute("resizeTableWidth", {
10613
- table: modelTable,
10614
- tableWidth: `${toPrecision(tableWidthAttributeNew)}%`,
10615
- columnWidths: columnWidthsAttributeNew
10616
- });
10617
- else editingView.change((writer) => {
10978
+ if (isColumnWidthsAttributeChanged || isTableWidthAttributeChanged) if (this._isResizingAllowed) {
10979
+ const tableWidthInPixels = toPrecision(tableWidthAttributeNew);
10980
+ const tableWidthInPixelsBeforeResize = toPrecision(tableWidthAttributeOld);
10981
+ const columnWidths = isPixelMode ? columnWidthsAttributeNew.map((width) => typeof width === "string" && !width.endsWith("px") ? `${toPrecision(parseFloat(width) / 100 * tableWidthInPixelsBeforeResize)}px` : width) : columnWidthsAttributeNew;
10982
+ editor.execute("resizeTableWidth", {
10983
+ table: modelTable,
10984
+ tableWidth: isPixelMode ? `${toPrecision(tableWidthInPixels)}px` : `${toPrecision(tableWidthAttributeNew)}%`,
10985
+ columnWidths
10986
+ });
10987
+ } else editingView.change((writer) => {
10618
10988
  if (columnWidthsAttributeOld) for (const viewCol of viewColumns) writer.setStyle("width", columnWidthsAttributeOld.shift(), viewCol);
10619
10989
  else writer.remove(viewColgroup);
10620
- if (isTableWidthAttributeChanged) if (tableWidthAttributeOld) writer.setStyle("width", tableWidthAttributeOld, viewFigure);
10621
- else writer.removeStyle("width", viewFigure);
10622
- if (!columnWidthsAttributeOld && !tableWidthAttributeOld) writer.removeClass("ck-table-resized", [...viewFigure.getChildren()].find((element) => element.name === "table"));
10990
+ if (isTableWidthAttributeChanged) this._setResizingTableWidth(writer, viewFigure, tableWidthAttributeOld || null);
10991
+ if (!columnWidthsAttributeOld && !tableWidthAttributeOld) writer.removeClass("ck-table-resized", viewTable);
10623
10992
  });
10624
10993
  editingView.change((writer) => {
10625
10994
  writer.removeClass("ck-table-column-resizer__active", viewResizer);
@@ -10652,6 +11021,7 @@ var TableColumnResizeEditing = class extends Plugin {
10652
11021
  const isRightEdge = leftColumnIndex === lastColumnIndex;
10653
11022
  const isLtrContent = editor.locale.contentLanguageDirection !== "rtl";
10654
11023
  const isTableCentered = tableAlignment === "center";
11024
+ const isPixelMode = isTableWidthInPixels(modelTable);
10655
11025
  const viewTable = viewLeftCell.findAncestor("table");
10656
11026
  const viewFigure = viewTable.findAncestor("figure");
10657
11027
  const viewColgroup = [...viewTable.getChildren()].find((viewCol) => viewCol.is("element", "colgroup"));
@@ -10662,24 +11032,30 @@ var TableColumnResizeEditing = class extends Plugin {
10662
11032
  const tableWidth = getTableWidthInPixels(modelTable, editor);
10663
11033
  const leftColumnWidth = columnWidths[leftColumnIndex];
10664
11034
  const rightColumnWidth = isRightEdge ? void 0 : columnWidths[leftColumnIndex + 1];
11035
+ const isTableWidthWithinContainerAtDragStart = tableWidth <= getEditableWidth(editor, modelTable.root.rootName);
11036
+ const tableScrollPlugin = editor.plugins.has("TableScrollEditing") ? editor.plugins.get("TableScrollEditing") : null;
10665
11037
  return {
10666
11038
  columnPosition,
10667
11039
  flags: {
10668
11040
  isRightEdge,
10669
11041
  isTableCentered,
10670
- isLtrContent
11042
+ isLtrContent,
11043
+ isPixelMode,
11044
+ isTableWidthWithinContainerAtDragStart,
11045
+ isTableScrollAllowed: !!tableScrollPlugin && tableScrollPlugin._isTableScrollable(modelTable)
10671
11046
  },
10672
11047
  elements: {
10673
11048
  viewResizer,
10674
11049
  modelTable,
10675
11050
  viewFigure,
11051
+ viewTable,
10676
11052
  viewColgroup,
10677
11053
  viewLeftColumn,
10678
11054
  viewRightColumn
10679
11055
  },
10680
11056
  widths: {
10681
- viewFigureParentWidth,
10682
11057
  viewFigureWidth,
11058
+ viewFigureParentWidth,
10683
11059
  tableWidth,
10684
11060
  leftColumnWidth,
10685
11061
  rightColumnWidth
@@ -10700,6 +11076,104 @@ var TableColumnResizeEditing = class extends Plugin {
10700
11076
  });
10701
11077
  }
10702
11078
  };
11079
+ /**
11080
+ * Tells whether the table wrapping the given view `<col>` element is sized in pixels. The width is read from the
11081
+ * column's own table (or its wrapping `<figure>`), never from an ancestor further up, so a width-less table nested
11082
+ * inside a wider one is not mistaken for a pixel table.
11083
+ */
11084
+ function isViewTableWidthInPixels(viewColElement) {
11085
+ const viewTable = viewColElement.findAncestor((element) => element.is("element", "table"));
11086
+ const viewParent = viewTable.parent;
11087
+ const width = (viewParent.is("element", "figure") ? viewParent : viewTable).getStyle("width");
11088
+ return typeof width === "string" && width.trim().endsWith("px");
11089
+ }
11090
+ /**
11091
+ * In the pixel mode keeps the sum of the column widths equal to the table width. If they differ (for example after a
11092
+ * manual table width change), the columns are scaled proportionally so that they sum up to the table width. In the
11093
+ * percentage mode (or when the columns already sum to the table width) it is a no-op.
11094
+ */
11095
+ function scalePixelColumnsToTableWidth(columnWidths, table) {
11096
+ if (!isTableWidthInPixels(table) || !isColumnWidthsInPixels(columnWidths)) return columnWidths;
11097
+ const tableWidthInPixels = parseFloat(table.getAttribute("tableWidth"));
11098
+ const totalWidth = sumArray(columnWidths);
11099
+ if (!totalWidth || Math.abs(totalWidth - tableWidthInPixels) < .5) return columnWidths;
11100
+ const factor = tableWidthInPixels / totalWidth;
11101
+ return columnWidths.map((width) => `${toPrecision(parseFloat(width) * factor)}px`);
11102
+ }
11103
+ /**
11104
+ * Normalizes a pixel-mode column group to concrete pixel widths. It mirrors {@link ~normalizeColumnWidths} (the
11105
+ * percentage path): percentages are resolved against the table width, and missing (`auto`/`undefined`) columns are
11106
+ * filled from the width left over in the table - so the column group never keeps `auto` widths or mixes units, and
11107
+ * downstream arithmetic never sees a non-pixel value.
11108
+ */
11109
+ function normalizePixelColumnWidths(columnWidths, table) {
11110
+ const tableWidthInPixels = parseFloat(table.getAttribute("tableWidth"));
11111
+ const pixelWidths = columnWidths.map((width) => {
11112
+ if (width === "auto" || width === void 0) return null;
11113
+ return width.endsWith("%") ? parseFloat(width) / 100 * tableWidthInPixels : parseFloat(width);
11114
+ });
11115
+ const missingColumns = pixelWidths.filter((width) => width === null).length;
11116
+ if (missingColumns) {
11117
+ const knownWidth = pixelWidths.reduce((sum, width) => width === null ? sum : sum + width, 0);
11118
+ const widthForMissingColumn = Math.max((tableWidthInPixels - knownWidth) / missingColumns, 40);
11119
+ return pixelWidths.map((width) => `${toPrecision(width === null ? widthForMissingColumn : width)}px`);
11120
+ }
11121
+ return pixelWidths.map((width) => `${toPrecision(width)}px`);
11122
+ }
11123
+ /**
11124
+ * Applies a column width in the pixel mode: the target column gets the absolute width and the table's own width
11125
+ * grows or shrinks to the sum of all column widths.
11126
+ */
11127
+ function applyPixelColumnWidths(writer, table, columnIndexes, value) {
11128
+ const columns = getTableColumnElements(table);
11129
+ const tableWidthInPixels = parseFloat(table.getAttribute("tableWidth"));
11130
+ const toPixels = (width) => width.trim().endsWith("%") ? parseFloat(width) / 100 * tableWidthInPixels : parseFloat(width);
11131
+ const targetPixels = Math.max(toPixels(value), 40);
11132
+ const widths = getTableColumnsWidths(table).map(toPixels);
11133
+ for (const index of columnIndexes) widths[index] = targetPixels;
11134
+ const roundedWidths = widths.map((width) => toPrecision(width));
11135
+ columns.forEach((columnElement, index) => {
11136
+ writer.setAttribute("columnWidth", `${roundedWidths[index]}px`, columnElement);
11137
+ });
11138
+ writer.setAttribute("tableWidth", `${toPrecision(sumArray(roundedWidths))}px`, table);
11139
+ }
11140
+ /**
11141
+ * Splits a sorted list of column indexes into maximal contiguous bands, for example `[ 0, 1, 3 ]` into `[ 0, 1 ]`
11142
+ * and `[ 3 ]`.
11143
+ */
11144
+ function getContiguousBands(columnIndexes) {
11145
+ const bands = [];
11146
+ for (const index of columnIndexes) {
11147
+ const lastBand = bands[bands.length - 1];
11148
+ if (lastBand && index === lastBand.end + 1) {
11149
+ lastBand.indexes.push(index);
11150
+ lastBand.end = index;
11151
+ } else bands.push({
11152
+ indexes: [index],
11153
+ end: index
11154
+ });
11155
+ }
11156
+ return bands;
11157
+ }
11158
+ /**
11159
+ * Given the table width a drag would naturally produce, returns the width that should actually be applied
11160
+ * once snapping and growth resistance around the container's width are taken into account:
11161
+ *
11162
+ * * if the natural width lands close to the container's width (on either side), it's pulled to exactly
11163
+ * match it,
11164
+ * * if the natural width is past the container's width, it stays pinned at the container's width until the
11165
+ * drag has gone far enough beyond it (the "resistance" zone) - past that point it keeps growing 1:1,
11166
+ * continuing smoothly from where the resistance was overcome instead of jumping,
11167
+ * * shrinking below the container's width is never resisted, only snapped when close.
11168
+ *
11169
+ * @internal
11170
+ */
11171
+ function applyContainerWidthResistance(naturalTableWidth, containerWidth) {
11172
+ const distance = naturalTableWidth - containerWidth;
11173
+ if (distance < 0) return -distance <= 5 ? containerWidth : naturalTableWidth;
11174
+ const resistanceZone = 5 + 10;
11175
+ return distance <= resistanceZone ? containerWidth : containerWidth + (distance - resistanceZone);
11176
+ }
10703
11177
 
10704
11178
  /**
10705
11179
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
@@ -10920,7 +11394,9 @@ var TableLayoutEditing = class extends Plugin {
10920
11394
  }
10921
11395
  }
10922
11396
  if (entry.type == "attribute" && entry.attributeKey == "tableType") {
10923
- for (const item of entry.range.getItems()) if (item.is("element", "table")) {
11397
+ for (const item of entry.range.getItems())
11398
+ /* v8 ignore else -- `tableType` is only ever set on a single `table`, so the range holds no other items. */
11399
+ if (item.is("element", "table")) {
10924
11400
  editor.model.schema.removeDisallowedAttributes([item], writer);
10925
11401
  const tableChildren = item.getChildren();
10926
11402
  for (const child of tableChildren) if (!editor.model.schema.checkChild(item, child)) {
@@ -11736,8 +12212,13 @@ function upcastTableAlignedDiv(defaultValue) {
11736
12212
  name: true,
11737
12213
  attributes: "align"
11738
12214
  })) return;
11739
- const viewTable = getViewTableFromWrapper(data.viewItem);
11740
- if (!viewTable || !conversionApi.consumable.test(viewTable, { name: true })) return;
12215
+ const significantChildren = Array.from(data.viewItem.getChildren()).filter((child) => {
12216
+ if (child.is("$text") || child.is("$textProxy")) return child.data.trim() !== "";
12217
+ return true;
12218
+ });
12219
+ if (significantChildren.length !== 1 || !significantChildren[0].is("element", "table")) return;
12220
+ const [viewTable] = significantChildren;
12221
+ if (!conversionApi.consumable.test(viewTable, { name: true })) return;
11741
12222
  conversionApi.consumable.consume(data.viewItem, {
11742
12223
  name: true,
11743
12224
  attributes: "align"
@@ -11757,7 +12238,6 @@ function upcastTableAlignedDiv(defaultValue) {
11757
12238
  }
11758
12239
  const align = convertToTableAlignment(data.viewItem.getAttribute("align"), viewTable.getAttribute("align"), getDefaultValueAdjusted(defaultValue, "", data));
11759
12240
  if (align) conversionApi.writer.setAttribute("tableAlignment", align, modelTable);
11760
- conversionApi.convertChildren(data.viewItem, conversionApi.writer.createPositionAt(modelTable, "end"));
11761
12241
  conversionApi.updateConversionResult(modelTable, data);
11762
12242
  });
11763
12243
  };
@@ -13076,10 +13556,378 @@ var TableCaption = class extends Plugin {
13076
13556
  }
13077
13557
  };
13078
13558
 
13559
+ /**
13560
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
13561
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
13562
+ */
13563
+ /**
13564
+ * @module table/tablescroll/watchers
13565
+ */
13566
+ /**
13567
+ * Creates a live collection of all `table` model elements present in the document and keeps it
13568
+ * up to date as tables are inserted, moved, or removed.
13569
+ *
13570
+ * @internal
13571
+ */
13572
+ function watchTableModelElements(model) {
13573
+ const tables = new Collection();
13574
+ model.document.on("change", () => {
13575
+ const documentChanges = model.document.differ.getChanges();
13576
+ const insertedTables = /* @__PURE__ */ new Set();
13577
+ const movedTables = /* @__PURE__ */ new Set();
13578
+ for (const change of documentChanges) if (change.type === "insert" && change.name !== "$text" && change.position.nodeAfter) {
13579
+ const range = model.createRangeOn(change.position.nodeAfter);
13580
+ for (const item of range.getItems()) {
13581
+ if (!item.is("element", "table")) continue;
13582
+ if (tables.has(item)) movedTables.add(item);
13583
+ else insertedTables.add(item);
13584
+ }
13585
+ }
13586
+ for (const table of Array.from(tables)) {
13587
+ if (table.root.rootName !== "$graveyard") continue;
13588
+ insertedTables.delete(table);
13589
+ movedTables.delete(table);
13590
+ if (tables.has(table)) tables.remove(table);
13591
+ }
13592
+ for (const table of movedTables)
13593
+ /* v8 ignore else -- @preserve */
13594
+ if (tables.has(table)) tables.remove(table);
13595
+ if (insertedTables.size || movedTables.size) tables.addMany([...insertedTables, ...movedTables]);
13596
+ });
13597
+ return tables;
13598
+ }
13599
+ /**
13600
+ * Observes the DOM width of every editing root and calls `onResize` whenever any of them changes width.
13601
+ * Height-only changes are ignored, since only a root's width can make a table overflow it.
13602
+ *
13603
+ * @internal
13604
+ */
13605
+ function watchRootsWidthResize(view, onResize) {
13606
+ const { roots } = view.document;
13607
+ const observedRoots = /* @__PURE__ */ new Map();
13608
+ const lastKnownWidths = /* @__PURE__ */ new Map();
13609
+ const attachRoot = (rootName) => {
13610
+ if (observedRoots.has(rootName)) return;
13611
+ const domRoot = view.getDomRoot(rootName);
13612
+ if (!domRoot) return;
13613
+ const observer = new ResizeObserver((entries) => {
13614
+ for (const entry of entries) {
13615
+ const width = entry.contentRect.width;
13616
+ if (lastKnownWidths.get(rootName) === width) continue;
13617
+ lastKnownWidths.set(rootName, width);
13618
+ onResize();
13619
+ }
13620
+ });
13621
+ observer.observe(domRoot);
13622
+ observedRoots.set(rootName, observer);
13623
+ };
13624
+ const detachRoot = (rootName) => {
13625
+ const observer = observedRoots.get(rootName);
13626
+ if (observer) {
13627
+ observer.disconnect();
13628
+ observedRoots.delete(rootName);
13629
+ lastKnownWidths.delete(rootName);
13630
+ }
13631
+ };
13632
+ const onRootAdd = (evt, viewRoot) => attachRoot(viewRoot.rootName);
13633
+ const onRootRemove = (evt, viewRoot) => detachRoot(viewRoot.rootName);
13634
+ const attachAllRoots = () => {
13635
+ for (const root of roots) attachRoot(root.rootName);
13636
+ };
13637
+ attachAllRoots();
13638
+ roots.on("add", onRootAdd);
13639
+ roots.on("remove", onRootRemove);
13640
+ view.on("render", attachAllRoots);
13641
+ return () => {
13642
+ roots.off("add", onRootAdd);
13643
+ roots.off("remove", onRootRemove);
13644
+ view.off("render", attachAllRoots);
13645
+ for (const observer of observedRoots.values()) observer.disconnect();
13646
+ observedRoots.clear();
13647
+ lastKnownWidths.clear();
13648
+ };
13649
+ }
13650
+
13651
+ /**
13652
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
13653
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
13654
+ */
13655
+ /**
13656
+ * @module table/tablescroll/tablescrollediting
13657
+ */
13658
+ /**
13659
+ * The table scrolling editing plugin.
13660
+ */
13661
+ var TableScrollEditing = class extends Plugin {
13662
+ /**
13663
+ * Used to listen to native DOM events.
13664
+ */
13665
+ _domEmitter = new (DomEmitterMixin())();
13666
+ /**
13667
+ * @inheritDoc
13668
+ */
13669
+ static get pluginName() {
13670
+ return "TableScrollEditing";
13671
+ }
13672
+ /**
13673
+ * @inheritDoc
13674
+ */
13675
+ static get isOfficialPlugin() {
13676
+ return true;
13677
+ }
13678
+ /**
13679
+ * @inheritDoc
13680
+ */
13681
+ static get requires() {
13682
+ return [TableEditing];
13683
+ }
13684
+ /**
13685
+ * @inheritDoc
13686
+ */
13687
+ constructor(editor) {
13688
+ super(editor);
13689
+ editor.config.define("table.tableScroll.tableTypes", ["content"]);
13690
+ }
13691
+ /**
13692
+ * @inheritDoc
13693
+ */
13694
+ init() {
13695
+ const { editor } = this;
13696
+ const tables = watchTableModelElements(editor.model);
13697
+ this._watchNewTables(tables);
13698
+ this._watchRootEditables(tables);
13699
+ this._registerConversion();
13700
+ this._watchColumnResize();
13701
+ this._watchFiguresScroll();
13702
+ }
13703
+ /**
13704
+ * @inheritDoc
13705
+ */
13706
+ destroy() {
13707
+ this._domEmitter.stopListening();
13708
+ super.destroy();
13709
+ }
13710
+ /**
13711
+ * Whether a given table may overflow its container and become horizontally scrollable, and whether
13712
+ * column/table resizing is allowed to grow it past the container's width.
13713
+ *
13714
+ * @internal
13715
+ */
13716
+ _isTableScrollable(table) {
13717
+ if (table.parent !== table.root) return false;
13718
+ const tableType = table.getAttribute("tableType") || "content";
13719
+ return this.editor.config.get("table.tableScroll.tableTypes").includes(tableType);
13720
+ }
13721
+ /**
13722
+ * Determines whether a table overflows its container and applies the corresponding view state:
13723
+ * the `ck-table-overflowing` class on the figure, and the actual (possibly container-exceeding)
13724
+ * width on the inner `<table>` and a sibling `<figcaption>`, if present.
13725
+ *
13726
+ * @internal
13727
+ */
13728
+ _updateTableScrollOverflowState(table, tableWidthOverride) {
13729
+ const { editor } = this;
13730
+ const containerWidth = getEditableWidth(editor, table.root.rootName);
13731
+ if (containerWidth === null) return;
13732
+ const viewFigure = editor.editing.mapper.toViewElement(table);
13733
+ if (!viewFigure) return;
13734
+ const viewTable = findChildElement(viewFigure, "table");
13735
+ if (!viewTable) return;
13736
+ const viewFigcaption = findChildElement(viewFigure, "figcaption");
13737
+ let tableWidth = null;
13738
+ let hasTableWidthSource = false;
13739
+ if (tableWidthOverride !== void 0) {
13740
+ tableWidth = tableWidthOverride;
13741
+ hasTableWidthSource = true;
13742
+ } else if (table.hasAttribute("tableWidth")) {
13743
+ tableWidth = table.getAttribute("tableWidth");
13744
+ hasTableWidthSource = true;
13745
+ }
13746
+ if (!hasTableWidthSource) return;
13747
+ const isOverflowing = !!tableWidth && this._isTableScrollable(table) && isTableWidthOverflowing(tableWidth, containerWidth);
13748
+ if (isOverflowing) this._scheduleScrollOffsetSync(viewFigure);
13749
+ editor.editing.view.change((writer) => {
13750
+ if (isOverflowing) {
13751
+ writer.removeStyle("width", viewFigure);
13752
+ writer.addClass("ck-table-overflowing", viewFigure);
13753
+ writer.setStyle("width", tableWidth, viewTable);
13754
+ if (viewFigcaption) writer.setStyle("width", tableWidth, viewFigcaption);
13755
+ } else {
13756
+ if (tableWidth) writer.setStyle("width", tableWidth, viewFigure);
13757
+ else writer.removeStyle("width", viewFigure);
13758
+ if (viewFigcaption) writer.removeStyle("width", viewFigcaption);
13759
+ writer.removeClass("ck-table-overflowing", viewFigure);
13760
+ writer.removeStyle("width", viewTable);
13761
+ writer.removeStyle("--ck-table-scroll-offset", viewFigure);
13762
+ }
13763
+ });
13764
+ }
13765
+ /**
13766
+ * Schedules a re-read of the widget figure's actual `scrollLeft` into `--ck-table-scroll-offset`, once
13767
+ * the render reflecting the change that triggered it has actually happened - see the call site for why
13768
+ * this can't just read (and force a reflow for) `scrollLeft` immediately instead.
13769
+ */
13770
+ _scheduleScrollOffsetSync(viewFigure) {
13771
+ const { view } = this.editor.editing;
13772
+ view.once("render", () => {
13773
+ const domFigure = view.domConverter.mapViewToDom(viewFigure);
13774
+ /* v8 ignore else -- @preserve */
13775
+ if (domFigure) view.change((writer) => {
13776
+ writer.setStyle("--ck-table-scroll-offset", `${domFigure.scrollLeft}px`, viewFigure);
13777
+ });
13778
+ });
13779
+ }
13780
+ /**
13781
+ * Registers editing-only downcast listeners that keep overflow state in sync.
13782
+ */
13783
+ _registerConversion() {
13784
+ const { editor } = this;
13785
+ editor.conversion.for("editingDowncast").add((dispatcher) => {
13786
+ dispatcher.on("attribute:tableWidth:table", (evt, data) => {
13787
+ this._updateTableScrollOverflowState(data.item, data.attributeNewValue);
13788
+ }, { priority: "low" });
13789
+ dispatcher.on("attribute:tableType:table", (evt, data) => {
13790
+ this._updateTableScrollOverflowState(data.item);
13791
+ }, { priority: "low" });
13792
+ dispatcher.on("insert:caption", (evt, data) => {
13793
+ const modelTable = data.item.parent;
13794
+ if (modelTable?.is("element", "table")) this._updateTableScrollOverflowState(modelTable);
13795
+ }, { priority: "lowest" });
13796
+ dispatcher.on("insert:table", (evt, data) => {
13797
+ this._updateTableScrollOverflowState(data.item);
13798
+ }, { priority: "lowest" });
13799
+ });
13800
+ }
13801
+ /**
13802
+ * Keeps every overflowing table's `--ck-table-scroll-offset` custom property in sync with its
13803
+ * `scrollLeft`, using a single delegated listener instead of one native listener per table.
13804
+ */
13805
+ _watchFiguresScroll() {
13806
+ const { editor } = this;
13807
+ const { view } = editor.editing;
13808
+ const onScroll = (evt, domEvent) => {
13809
+ const domFigure = domEvent.target;
13810
+ if (!domFigure.classList?.contains("ck-table-overflowing")) return;
13811
+ const viewFigure = view.domConverter.mapDomToView(domFigure);
13812
+ if (!viewFigure) return;
13813
+ view.change((writer) => {
13814
+ writer.setStyle("--ck-table-scroll-offset", `${domFigure.scrollLeft}px`, viewFigure);
13815
+ });
13816
+ };
13817
+ this._domEmitter.listenTo(global.document, "scroll", onScroll, { useCapture: true });
13818
+ }
13819
+ /**
13820
+ * Listen for column resize events and updates proper view element size.
13821
+ */
13822
+ _watchColumnResize() {
13823
+ const { editor } = this;
13824
+ const { plugins } = editor;
13825
+ if (!plugins.has("TableColumnResizeEditing")) return;
13826
+ const columnResizeEditing = plugins.get("TableColumnResizeEditing");
13827
+ this.listenTo(columnResizeEditing, "_setResizingTableWidth", (evt, [, viewFigure, width]) => {
13828
+ evt.stop();
13829
+ const modelTable = editor.editing.mapper.toModelElement(viewFigure);
13830
+ this._updateTableScrollOverflowState(modelTable, width);
13831
+ }, { priority: "high" });
13832
+ this.listenTo(columnResizeEditing, "_getResizingTableWidth", (evt, [viewFigure]) => {
13833
+ evt.stop();
13834
+ evt.return = getWidthHoldingElement(viewFigure).getStyle("width");
13835
+ }, { priority: "high" });
13836
+ }
13837
+ /**
13838
+ * Re-evaluates the overflow state of every table whenever the window or an editing root is resized.
13839
+ */
13840
+ _watchRootEditables(tables) {
13841
+ const { editor } = this;
13842
+ const recalculateAll = () => {
13843
+ const resizingTable = getCurrentlyResizingTable(editor);
13844
+ for (const table of tables) {
13845
+ if (table === resizingTable) continue;
13846
+ this._updateTableScrollOverflowState(table);
13847
+ }
13848
+ };
13849
+ const throttledRecalculateAll = throttle(recalculateAll, 100);
13850
+ const stopWatchingRootsResize = watchRootsWidthResize(editor.editing.view, throttledRecalculateAll);
13851
+ editor.ui.view.listenTo(global.window, "resize", throttledRecalculateAll);
13852
+ editor.once("ready", recalculateAll);
13853
+ this.listenTo(editor, "destroy", () => {
13854
+ throttledRecalculateAll.cancel();
13855
+ stopWatchingRootsResize();
13856
+ });
13857
+ }
13858
+ /**
13859
+ * Evaluates the overflow state of every newly inserted table.
13860
+ */
13861
+ _watchNewTables(tables) {
13862
+ this.listenTo(tables, "change", (evt, data) => {
13863
+ for (const table of data.added) this._updateTableScrollOverflowState(table);
13864
+ });
13865
+ }
13866
+ };
13867
+ /**
13868
+ * Returns the table for which a column resize is currently in progress, if the
13869
+ * `TableColumnResizeEditing` plugin is loaded and such a resize is active.
13870
+ */
13871
+ function getCurrentlyResizingTable(editor) {
13872
+ const { plugins } = editor;
13873
+ if (!plugins.has("TableColumnResizeEditing")) return null;
13874
+ return plugins.get("TableColumnResizeEditing").resizingTable;
13875
+ }
13876
+ /**
13877
+ * Checks if given table width overflows container width.
13878
+ */
13879
+ function isTableWidthOverflowing(tableWidth, containerWidth) {
13880
+ if (tableWidth.endsWith("%")) return parseFloat(tableWidth) > 100;
13881
+ if (tableWidth.endsWith("px")) return parseFloat(tableWidth) > containerWidth;
13882
+ return false;
13883
+ }
13884
+ /**
13885
+ * Returns whichever element - the widget's `<figure>` or its inner `<table>` - currently holds the
13886
+ * table's actual (possibly container-exceeding) width: the `<table>` while overflowing, the `<figure>`
13887
+ * otherwise.
13888
+ */
13889
+ function getWidthHoldingElement(viewFigure) {
13890
+ return viewFigure.hasClass("ck-table-overflowing") ? findChildElement(viewFigure, "table") : viewFigure;
13891
+ }
13892
+ /**
13893
+ * Finds a direct child element of `parent` by its name.
13894
+ */
13895
+ function findChildElement(parent, elementName) {
13896
+ return Array.from(parent.getChildren()).find((child) => child.is("element", elementName));
13897
+ }
13898
+
13899
+ /**
13900
+ * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
13901
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
13902
+ */
13903
+ /**
13904
+ * @module table/tablescroll
13905
+ */
13906
+ var TableScroll = class extends Plugin {
13907
+ /**
13908
+ * @inheritDoc
13909
+ */
13910
+ static get pluginName() {
13911
+ return "TableScroll";
13912
+ }
13913
+ /**
13914
+ * @inheritDoc
13915
+ */
13916
+ static get isOfficialPlugin() {
13917
+ return true;
13918
+ }
13919
+ /**
13920
+ * @inheritDoc
13921
+ */
13922
+ static get requires() {
13923
+ return [TableScrollEditing];
13924
+ }
13925
+ };
13926
+
13079
13927
  /**
13080
13928
  * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
13081
13929
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
13082
13930
  */
13083
13931
 
13084
- export { InsertColumnCommand, InsertRowCommand, InsertTableCommand, InsertTableLayoutCommand, MergeCellCommand, MergeCellsCommand, PlainTableOutput, RemoveColumnCommand, RemoveRowCommand, SelectColumnCommand, SelectRowCommand, SetFooterRowCommand, SetHeaderColumnCommand, SetHeaderRowCommand, SplitCellCommand, Table, TableAlignmentCommand, TableBackgroundColorCommand, TableBorderColorCommand, TableBorderStyleCommand, TableBorderWidthCommand, TableCaption, TableCaptionEditing, TableCaptionUI, TableCellBackgroundColorCommand, TableCellBorderColorCommand, TableCellBorderStyleCommand, TableCellBorderWidthCommand, TableCellHeightCommand, TableCellHorizontalAlignmentCommand, TableCellPaddingCommand, TableCellProperties, TableCellPropertiesEditing, TableCellPropertiesUI, TableCellPropertiesView, TableCellPropertyCommand, TableCellTypeCommand, TableCellVerticalAlignmentCommand, TableCellWidthCommand, TableCellWidthEditing, TableClipboard, TableColumnResize, TableColumnResizeEditing, TableEditing, TableHeightCommand, TableKeyboard, TableLayout, TableLayoutEditing, TableLayoutUI, TableMouse, TableProperties, TablePropertiesEditing, TablePropertiesUI, TablePropertiesView, TablePropertyCommand, TableSelection, TableSlot, TableToolbar, TableTypeCommand, TableUI, TableUtils, TableWalker, TableWidthCommand, TableWidthsCommand, ToggleTableCaptionCommand, InsertTableView as _InsertTableView, COLUMN_MIN_WIDTH_AS_PERCENTAGE as _TABLE_COLUMN_MIN_WIDTH_AS_PERCENTAGE, COLUMN_MIN_WIDTH_IN_PIXELS as _TABLE_COLUMN_MIN_WIDTH_IN_PIXELS, COLUMN_RESIZE_DISTANCE_THRESHOLD as _TABLE_COLUMN_RESIZE_DISTANCE_THRESHOLD, COLUMN_WIDTH_PRECISION as _TABLE_COLUMN_WIDTH_PRECISION, defaultColors as _TABLE_DEFAULT_COLORS, ColorInputView as _TableColorInputView, MouseEventsObserver as _TableMouseEventsObserver, addDefaultUnitToNumericValue as _addDefaultUnitToNumericValue, adjustLastColumnIndex as _adjustLastTableColumnIndex, adjustLastRowIndex as _adjustLastTableRowIndex, clamp as _clamp, colorFieldValidator as _colorTableFieldValidator, convertParagraphInTableCell as _convertParagraphInTableCell, createEmptyTableCell as _createEmptyTableCell, createFilledArray as _createFilledArray, cropTableToDimensions as _cropTableToDimensions, downcastTable as _downcastTable, downcastTableAttribute as _downcastTableAttribute, downcastAttributeToStyle as _downcastTableAttributeToStyle, downcastCell as _downcastTableCell, downcastTableResizedClass as _downcastTableResizedClass, downcastRow as _downcastTableRow, enableProperty as _enableTableCellProperty, ensureParagraphInTableCell as _ensureParagraphInTableCell, fillToolbar as _fillTableOrCellToolbar, getBalloonCellPositionData as _getBalloonTableCellPositionData, getBalloonTablePositionData as _getBalloonTablePositionData, getBorderStyleLabels as _getBorderTableStyleLabels, getChangedResizedTables as _getChangedResizedTables, getDefaultValueAdjusted as _getDefaultTableValueAdjusted, getDomCellOuterWidth as _getDomTableCellOuterWidth, getElementWidthInPixels as _getElementWidthInPixels, getHorizontallyOverlappingCells as _getHorizontallyOverlappingTableCells, getLabeledColorInputCreator as _getLabeledTableColorInputCreator, getLocalizedColorErrorText as _getLocalizedTableColorErrorText, getLocalizedLengthErrorText as _getLocalizedTableLengthErrorText, getNormalizedDefaultProperties as _getNormalizedDefaultTableBaseProperties, getNormalizedDefaultCellProperties as _getNormalizedDefaultTableCellProperties, getNormalizedDefaultTableProperties as _getNormalizedDefaultTableProperties, getSelectedTableWidget as _getSelectedTableWidget, getSelectionAffectedTable as _getSelectionAffectedTable, getSelectionAffectedTableWidget as _getSelectionAffectedTableWidget, getSingleValue as _getTableBorderBoxSingleValue, getCaptionFromTableModelElement as _getTableCaptionFromModelElement, getCaptionFromModelSelection as _getTableCaptionFromModelSelection, getColumnEdgesIndexes as _getTableColumnEdgesIndexes, getTableColumnElements as _getTableColumnElements, getColumnGroupElement as _getTableColumnGroupElement, getColumnMinWidthAsPercentage as _getTableColumnMinWidthAsPercentage, getTableColumnsWidths as _getTableColumnsWidths, getBorderStyleDefinitions as _getTableOrCellBorderStyleDefinitions, getTableWidgetAncestor as _getTableWidgetAncestor, getTableWidthInPixels as _getTableWidthInPixels, getVerticallyOverlappingCells as _getVerticallyOverlappingTableCells, injectTableCaptionPostFixer as _injectTableCaptionPostFixer, injectTableCellParagraphPostFixer as _injectTableCellParagraphPostFixer, injectTableLayoutPostFixer as _injectTableLayoutPostFixer, isSingleParagraphWithoutAttributes as _isSingleTableParagraphWithoutAttributes, isHeadingColumnCell as _isTableHeadingColumnCell, isTable as _isTableModelElement, lengthFieldValidator as _lengthTableFieldValidator, lineWidthFieldValidator as _lineWidthTableFieldValidator, matchTableCaptionViewElement as _matchTableCaptionViewElement, normalizeColumnWidths as _normalizeTableColumnWidths, removeEmptyColumns as _removeEmptyTableColumns, removeEmptyRows as _removeEmptyTableRows, removeEmptyRowsColumns as _removeEmptyTableRowsColumns, repositionContextualBalloon as _repositionTableContextualBalloon, skipEmptyTableRow as _skipEmptyTableRow, splitHorizontally as _splitTableCellHorizontally, splitVertically as _splitTableCellVertically, sumArray as _sumArray, tableCellRefreshHandler as _tableCellRefreshHandler, tableStructureRefreshHandler as _tableStructureRefreshHandler, toPrecision as _toPrecision, translateColSpanAttribute as _translateTableColspanAttribute, trimTableCellIfNeeded as _trimTableCellIfNeeded, upcastStyleToAttribute as _upcastNormalizedTableStyleToAttribute, upcastTable as _upcastTable, upcastBorderStyles as _upcastTableBorderStyles, upcastColgroupElement as _upcastTableColgroupElement, upcastTableFigure as _upcastTableFigure, updateColumnElements as _updateTableColumnElements, updateNumericAttribute as _updateTableNumericAttribute, isTableHeaderCellType };
13932
+ export { InsertColumnCommand, InsertRowCommand, InsertTableCommand, InsertTableLayoutCommand, MergeCellCommand, MergeCellsCommand, PlainTableOutput, RemoveColumnCommand, RemoveRowCommand, SelectColumnCommand, SelectRowCommand, SetFooterRowCommand, SetHeaderColumnCommand, SetHeaderRowCommand, SplitCellCommand, Table, TableAlignmentCommand, TableBackgroundColorCommand, TableBorderColorCommand, TableBorderStyleCommand, TableBorderWidthCommand, TableCaption, TableCaptionEditing, TableCaptionUI, TableCellBackgroundColorCommand, TableCellBorderColorCommand, TableCellBorderStyleCommand, TableCellBorderWidthCommand, TableCellHeightCommand, TableCellHorizontalAlignmentCommand, TableCellPaddingCommand, TableCellProperties, TableCellPropertiesEditing, TableCellPropertiesUI, TableCellPropertiesView, TableCellPropertyCommand, TableCellTypeCommand, TableCellVerticalAlignmentCommand, TableCellWidthCommand, TableCellWidthEditing, TableClipboard, TableColumnResize, TableColumnResizeEditing, TableColumnWidthCommand, TableEditing, TableHeightCommand, TableKeyboard, TableLayout, TableLayoutEditing, TableLayoutUI, TableMouse, TableProperties, TablePropertiesEditing, TablePropertiesUI, TablePropertiesView, TablePropertyCommand, TableScroll, TableScrollEditing, TableSelection, TableSlot, TableToolbar, TableTypeCommand, TableUI, TableUtils, TableWalker, TableWidthCommand, TableWidthsCommand, ToggleTableCaptionCommand, InsertTableView as _InsertTableView, COLUMN_MIN_WIDTH_AS_PERCENTAGE as _TABLE_COLUMN_MIN_WIDTH_AS_PERCENTAGE, COLUMN_MIN_WIDTH_IN_PIXELS as _TABLE_COLUMN_MIN_WIDTH_IN_PIXELS, COLUMN_RESIZE_DISTANCE_THRESHOLD as _TABLE_COLUMN_RESIZE_DISTANCE_THRESHOLD, COLUMN_WIDTH_PRECISION as _TABLE_COLUMN_WIDTH_PRECISION, defaultColors as _TABLE_DEFAULT_COLORS, ColorInputView as _TableColorInputView, MouseEventsObserver as _TableMouseEventsObserver, addDefaultUnitToNumericValue as _addDefaultUnitToNumericValue, adjustLastColumnIndex as _adjustLastTableColumnIndex, adjustLastRowIndex as _adjustLastTableRowIndex, clamp as _clamp, colorFieldValidator as _colorTableFieldValidator, convertParagraphInTableCell as _convertParagraphInTableCell, createEmptyTableCell as _createEmptyTableCell, createFilledArray as _createFilledArray, cropTableToDimensions as _cropTableToDimensions, downcastTable as _downcastTable, downcastTableAttribute as _downcastTableAttribute, downcastAttributeToStyle as _downcastTableAttributeToStyle, downcastCell as _downcastTableCell, downcastTableResizedClass as _downcastTableResizedClass, downcastRow as _downcastTableRow, enableProperty as _enableTableCellProperty, ensureParagraphInTableCell as _ensureParagraphInTableCell, fillToolbar as _fillTableOrCellToolbar, getBalloonCellPositionData as _getBalloonTableCellPositionData, getBalloonTablePositionData as _getBalloonTablePositionData, getBorderStyleLabels as _getBorderTableStyleLabels, getChangedResizedTables as _getChangedResizedTables, getDefaultValueAdjusted as _getDefaultTableValueAdjusted, getDomCellOuterWidth as _getDomTableCellOuterWidth, getElementWidthInPixels as _getElementWidthInPixels, getHorizontallyOverlappingCells as _getHorizontallyOverlappingTableCells, getLabeledColorInputCreator as _getLabeledTableColorInputCreator, getLocalizedColorErrorText as _getLocalizedTableColorErrorText, getLocalizedLengthErrorText as _getLocalizedTableLengthErrorText, getNormalizedDefaultProperties as _getNormalizedDefaultTableBaseProperties, getNormalizedDefaultCellProperties as _getNormalizedDefaultTableCellProperties, getNormalizedDefaultTableProperties as _getNormalizedDefaultTableProperties, getSelectedTableWidget as _getSelectedTableWidget, getSelectionAffectedTable as _getSelectionAffectedTable, getSelectionAffectedTableWidget as _getSelectionAffectedTableWidget, getSingleValue as _getTableBorderBoxSingleValue, getCaptionFromTableModelElement as _getTableCaptionFromModelElement, getCaptionFromModelSelection as _getTableCaptionFromModelSelection, getColumnEdgesIndexes as _getTableColumnEdgesIndexes, getTableColumnElements as _getTableColumnElements, getColumnGroupElement as _getTableColumnGroupElement, getColumnMinWidthAsPercentage as _getTableColumnMinWidthAsPercentage, getTableColumnsWidths as _getTableColumnsWidths, getBorderStyleDefinitions as _getTableOrCellBorderStyleDefinitions, getTableWidgetAncestor as _getTableWidgetAncestor, getTableWidthInPixels as _getTableWidthInPixels, getVerticallyOverlappingCells as _getVerticallyOverlappingTableCells, injectTableCaptionPostFixer as _injectTableCaptionPostFixer, injectTableCellParagraphPostFixer as _injectTableCellParagraphPostFixer, injectTableLayoutPostFixer as _injectTableLayoutPostFixer, isSingleParagraphWithoutAttributes as _isSingleTableParagraphWithoutAttributes, isHeadingColumnCell as _isTableHeadingColumnCell, isTable as _isTableModelElement, lengthFieldValidator as _lengthTableFieldValidator, lineWidthFieldValidator as _lineWidthTableFieldValidator, matchTableCaptionViewElement as _matchTableCaptionViewElement, normalizeColumnWidths as _normalizeTableColumnWidths, removeEmptyColumns as _removeEmptyTableColumns, removeEmptyRows as _removeEmptyTableRows, removeEmptyRowsColumns as _removeEmptyTableRowsColumns, repositionContextualBalloon as _repositionTableContextualBalloon, skipEmptyTableRow as _skipEmptyTableRow, splitHorizontally as _splitTableCellHorizontally, splitVertically as _splitTableCellVertically, sumArray as _sumArray, tableCellRefreshHandler as _tableCellRefreshHandler, tableStructureRefreshHandler as _tableStructureRefreshHandler, toPrecision as _toPrecision, translateColSpanAttribute as _translateTableColspanAttribute, trimTableCellIfNeeded as _trimTableCellIfNeeded, upcastStyleToAttribute as _upcastNormalizedTableStyleToAttribute, upcastTable as _upcastTable, upcastBorderStyles as _upcastTableBorderStyles, upcastColgroupElement as _upcastTableColgroupElement, upcastTableFigure as _upcastTableFigure, updateColumnElements as _updateTableColumnElements, updateNumericAttribute as _updateTableNumericAttribute, isTableHeaderCellType };
13085
13933
  //# sourceMappingURL=index.js.map