@underverse-ui/underverse 2.0.28 → 2.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ueditor.cjs CHANGED
@@ -4343,6 +4343,172 @@ var init_url_safety = __esm({
4343
4343
  }
4344
4344
  });
4345
4345
 
4346
+ // src/components/UEditor/table-width-model.ts
4347
+ function positiveNumber(value, fallback) {
4348
+ const number = Number(value);
4349
+ return Number.isFinite(number) && number > 0 ? number : fallback;
4350
+ }
4351
+ function clampTableBasisPoints(value, fallback = TABLE_WIDTH_BASIS_POINTS, maximum = TABLE_WIDTH_BASIS_POINTS) {
4352
+ const number = Number(value);
4353
+ if (!Number.isFinite(number)) return fallback;
4354
+ return Math.min(maximum, Math.max(0, Math.round(number)));
4355
+ }
4356
+ function clampResponsiveTableWidthBp(value, fallback = DEFAULT_RESPONSIVE_TABLE_WIDTH_BP) {
4357
+ return Math.max(1, clampTableBasisPoints(value, fallback, MAX_RESPONSIVE_TABLE_WIDTH_BP));
4358
+ }
4359
+ function normalizeTableWidthMode(value) {
4360
+ return value === "responsive" || value === "full" ? "responsive" : "fixed";
4361
+ }
4362
+ function parsePercentageToBasisPoints(value, maximum = MAX_RESPONSIVE_TABLE_WIDTH_BP) {
4363
+ if (!value) return null;
4364
+ const match = value.trim().match(/^(-?\d+(?:\.\d+)?)%$/);
4365
+ if (!match) return null;
4366
+ const percentage = Number.parseFloat(match[1]);
4367
+ if (!Number.isFinite(percentage)) return null;
4368
+ return clampTableBasisPoints(percentage * 100, TABLE_WIDTH_BASIS_POINTS, maximum);
4369
+ }
4370
+ function formatBasisPointsAsPercentage(value) {
4371
+ const percentage = clampTableBasisPoints(value, 0, MAX_RESPONSIVE_TABLE_WIDTH_BP) / 100;
4372
+ return `${Number.parseFloat(percentage.toFixed(2))}%`;
4373
+ }
4374
+ function parseColumnRatios(value) {
4375
+ const parts = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
4376
+ const ratios = parts.map((part) => Number(part));
4377
+ return ratios.length > 0 && ratios.every((ratio) => Number.isFinite(ratio) && ratio > 0) ? ratios : null;
4378
+ }
4379
+ function normalizeColumnRatios(values, columnCount) {
4380
+ if (columnCount <= 0) return [];
4381
+ const source = values?.length === columnCount ? values.map((value) => positiveNumber(value, 1)) : Array.from({ length: columnCount }, () => 1);
4382
+ const sourceTotal = source.reduce((sum, value) => sum + value, 0);
4383
+ const normalized = source.map((value) => Math.max(1, Math.round(value / sourceTotal * TABLE_WIDTH_BASIS_POINTS)));
4384
+ let difference = TABLE_WIDTH_BASIS_POINTS - normalized.reduce((sum, value) => sum + value, 0);
4385
+ while (difference !== 0) {
4386
+ let changed = false;
4387
+ for (let index = normalized.length - 1; index >= 0 && difference !== 0; index -= 1) {
4388
+ if (difference < 0 && normalized[index] <= 1) continue;
4389
+ normalized[index] += difference > 0 ? 1 : -1;
4390
+ difference += difference > 0 ? -1 : 1;
4391
+ changed = true;
4392
+ }
4393
+ if (!changed) break;
4394
+ }
4395
+ return normalized;
4396
+ }
4397
+ function getLogicalTableColumnCount(table) {
4398
+ const firstRow = table.firstChild;
4399
+ if (!firstRow) return 0;
4400
+ let count = 0;
4401
+ firstRow.forEach((cell) => {
4402
+ count += Math.max(1, Number(cell.attrs.colspan) || 1);
4403
+ });
4404
+ return count;
4405
+ }
4406
+ function getLegacyTableColumnWeights(table, fallback = 100) {
4407
+ const weights = [];
4408
+ const firstRow = table.firstChild;
4409
+ if (!firstRow) return weights;
4410
+ firstRow.forEach((cell) => {
4411
+ const colspan = Math.max(1, Number(cell.attrs.colspan) || 1);
4412
+ const colwidth = Array.isArray(cell.attrs.colwidth) ? cell.attrs.colwidth : [];
4413
+ for (let index = 0; index < colspan; index += 1) {
4414
+ weights.push(positiveNumber(colwidth[index], fallback));
4415
+ }
4416
+ });
4417
+ return weights;
4418
+ }
4419
+ function getTableColumnRatios(table) {
4420
+ const columnCount = getLogicalTableColumnCount(table);
4421
+ const stored = parseColumnRatios(table.attrs.columnRatios);
4422
+ return normalizeColumnRatios(
4423
+ stored?.length === columnCount ? stored : getLegacyTableColumnWeights(table),
4424
+ columnCount
4425
+ );
4426
+ }
4427
+ function getResponsiveTableWidthBp(table) {
4428
+ return clampResponsiveTableWidthBp(table.attrs.widthBp);
4429
+ }
4430
+ function getResponsiveTableOffsetBp(table) {
4431
+ const width = getResponsiveTableWidthBp(table);
4432
+ return resolveResponsiveTableOffsetBp(width, null, table.attrs.offsetBp);
4433
+ }
4434
+ function resolveResponsiveTableOffsetBp(widthBp, tableAlign, fallbackOffsetBp = 0) {
4435
+ const availableGap = Math.max(
4436
+ 0,
4437
+ TABLE_WIDTH_BASIS_POINTS - clampResponsiveTableWidthBp(widthBp)
4438
+ );
4439
+ if (tableAlign === "center") return Math.round(availableGap / 2);
4440
+ if (tableAlign === "right") return availableGap;
4441
+ if (tableAlign === "left") return 0;
4442
+ return Math.min(availableGap, clampTableBasisPoints(fallbackOffsetBp, 0));
4443
+ }
4444
+ function insertResponsiveColumnRatio(ratios, insertIndex, sourceIndex) {
4445
+ if (ratios.length === 0) return [TABLE_WIDTH_BASIS_POINTS];
4446
+ const safeSourceIndex = Math.max(0, Math.min(sourceIndex, ratios.length - 1));
4447
+ const next = [...ratios];
4448
+ const sourceRatio = next[safeSourceIndex];
4449
+ next.splice(Math.max(0, Math.min(insertIndex, next.length)), 0, sourceRatio);
4450
+ return normalizeColumnRatios(next, next.length);
4451
+ }
4452
+ function deleteResponsiveColumnRatio(ratios, deleteIndex) {
4453
+ if (ratios.length <= 1) return [];
4454
+ const next = ratios.filter((_, index) => index !== deleteIndex);
4455
+ return normalizeColumnRatios(next, next.length);
4456
+ }
4457
+ function moveResponsiveColumnRatio(ratios, from, to) {
4458
+ if (from === to || from < 0 || from >= ratios.length || to < 0 || to >= ratios.length) return [...ratios];
4459
+ const next = [...ratios];
4460
+ const [moved] = next.splice(from, 1);
4461
+ next.splice(to, 0, moved);
4462
+ return normalizeColumnRatios(next, next.length);
4463
+ }
4464
+ function insertResponsiveColumnLayout(widthBp, ratios, insertIndex, sourceIndex) {
4465
+ if (ratios.length === 0) {
4466
+ return {
4467
+ widthBp: clampResponsiveTableWidthBp(widthBp),
4468
+ columnRatios: [TABLE_WIDTH_BASIS_POINTS]
4469
+ };
4470
+ }
4471
+ const normalized = normalizeColumnRatios(ratios, ratios.length);
4472
+ const safeSourceIndex = Math.max(0, Math.min(sourceIndex, normalized.length - 1));
4473
+ const sourceRatio = normalized[safeSourceIndex];
4474
+ return {
4475
+ // Growing by the source column's share preserves every existing column's
4476
+ // rendered width instead of squeezing the whole table back into 100%.
4477
+ widthBp: clampResponsiveTableWidthBp(
4478
+ clampResponsiveTableWidthBp(widthBp) * (TABLE_WIDTH_BASIS_POINTS + sourceRatio) / TABLE_WIDTH_BASIS_POINTS
4479
+ ),
4480
+ columnRatios: insertResponsiveColumnRatio(normalized, insertIndex, safeSourceIndex)
4481
+ };
4482
+ }
4483
+ function deleteResponsiveColumnLayout(widthBp, ratios, deleteIndex) {
4484
+ const normalized = normalizeColumnRatios(ratios, ratios.length);
4485
+ if (normalized.length <= 1 || deleteIndex < 0 || deleteIndex >= normalized.length) {
4486
+ return {
4487
+ widthBp: clampResponsiveTableWidthBp(widthBp),
4488
+ columnRatios: normalized.length <= 1 ? [] : normalized
4489
+ };
4490
+ }
4491
+ const remainingRatio = TABLE_WIDTH_BASIS_POINTS - normalized[deleteIndex];
4492
+ const nextWidthBp = clampResponsiveTableWidthBp(
4493
+ clampResponsiveTableWidthBp(widthBp) * remainingRatio / TABLE_WIDTH_BASIS_POINTS
4494
+ );
4495
+ return {
4496
+ // Deletion is the inverse operation: remaining columns keep their rendered
4497
+ // widths while the table gives the removed column's space back.
4498
+ widthBp: Math.abs(nextWidthBp - DEFAULT_RESPONSIVE_TABLE_WIDTH_BP) <= 1 ? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP : nextWidthBp,
4499
+ columnRatios: deleteResponsiveColumnRatio(normalized, deleteIndex)
4500
+ };
4501
+ }
4502
+ var TABLE_WIDTH_BASIS_POINTS, DEFAULT_RESPONSIVE_TABLE_WIDTH_BP, MAX_RESPONSIVE_TABLE_WIDTH_BP;
4503
+ var init_table_width_model = __esm({
4504
+ "src/components/UEditor/table-width-model.ts"() {
4505
+ "use strict";
4506
+ TABLE_WIDTH_BASIS_POINTS = 1e4;
4507
+ DEFAULT_RESPONSIVE_TABLE_WIDTH_BP = TABLE_WIDTH_BASIS_POINTS;
4508
+ MAX_RESPONSIVE_TABLE_WIDTH_BP = 1e5;
4509
+ }
4510
+ });
4511
+
4346
4512
  // src/components/UEditor/clipboard-tables.ts
4347
4513
  function getClipboardData(dataTransfer, type) {
4348
4514
  try {
@@ -4959,13 +5125,34 @@ function createNormalizedRowContent(row, columnCount, fillerCellAttrs) {
4959
5125
  }
4960
5126
  return content;
4961
5127
  }
4962
- function createTableContent(rows, minColumnCount = 1, fillerCellAttrs) {
5128
+ function createTableContent(rows, minColumnCount = 1, fillerCellAttrs, layout) {
4963
5129
  const tableRows = rows.filter((row) => row.cells.length > 0);
4964
5130
  if (tableRows.length === 0) return null;
4965
5131
  const { positionedRows, columnCount } = normalizeTableRows(tableRows);
4966
5132
  if (columnCount < minColumnCount) return null;
5133
+ const inferredColumnWeights = Array.from({ length: columnCount }, () => 100);
5134
+ positionedRows.forEach((row) => {
5135
+ row.cells.forEach(({ cell, colspan, startColumn }) => {
5136
+ const width = colspan === 1 ? cell.attrs?.colwidth?.[0] : null;
5137
+ if (typeof width === "number" && Number.isFinite(width) && width > 0) {
5138
+ inferredColumnWeights[startColumn] = width;
5139
+ }
5140
+ });
5141
+ });
5142
+ const columnRatios = normalizeColumnRatios(
5143
+ layout?.columnRatios?.length === columnCount ? layout.columnRatios : inferredColumnWeights,
5144
+ columnCount
5145
+ );
5146
+ const widthBp = clampResponsiveTableWidthBp(layout?.widthBp, DEFAULT_RESPONSIVE_TABLE_WIDTH_BP);
5147
+ const offsetBp = resolveResponsiveTableOffsetBp(widthBp, null, layout?.offsetBp);
4967
5148
  return {
4968
5149
  type: "table",
5150
+ attrs: {
5151
+ widthMode: "responsive",
5152
+ widthBp,
5153
+ offsetBp,
5154
+ columnRatios
5155
+ },
4969
5156
  content: positionedRows.map((row) => ({
4970
5157
  type: "tableRow",
4971
5158
  ...row.attrs ? { attrs: row.attrs } : {},
@@ -4986,9 +5173,22 @@ function getClipboardTableContent(dataTransfer) {
4986
5173
  if (tables.length !== 1 || hasMeaningfulContentOutsideTable(sourceBody)) return null;
4987
5174
  const table = tables[0];
4988
5175
  if (!(table instanceof HTMLTableElement)) return null;
4989
- return createTableContent(getHtmlTableRows(table, styleMap), 1, {
4990
- backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
4991
- });
5176
+ const storedWidthValue = table.getAttribute("data-table-width-bp");
5177
+ const storedOffsetValue = table.getAttribute("data-table-offset-bp");
5178
+ const storedWidthBp = Number(storedWidthValue);
5179
+ const storedOffsetBp = Number(storedOffsetValue);
5180
+ const widthBp = storedWidthValue !== null && Number.isFinite(storedWidthBp) && storedWidthBp > 0 ? storedWidthBp : parsePercentageToBasisPoints(table.getAttribute("data-table-width") ?? table.style.width) ?? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP;
5181
+ const offsetBp = storedOffsetValue !== null && Number.isFinite(storedOffsetBp) && storedOffsetBp >= 0 ? storedOffsetBp : parsePercentageToBasisPoints(table.getAttribute("data-table-offset") ?? table.style.marginLeft) ?? 0;
5182
+ return createTableContent(
5183
+ getHtmlTableRows(table, styleMap),
5184
+ 1,
5185
+ { backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR },
5186
+ {
5187
+ widthBp,
5188
+ offsetBp,
5189
+ columnRatios: parseColumnRatios(table.getAttribute("data-table-column-ratios"))
5190
+ }
5191
+ );
4992
5192
  }
4993
5193
  function parseClipboardTsvRows(text) {
4994
5194
  const rows = [];
@@ -5055,6 +5255,7 @@ var DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR, DEFAULT_HTML_TABLE_TEXT_COLOR, BOR
5055
5255
  var init_clipboard_tables = __esm({
5056
5256
  "src/components/UEditor/clipboard-tables.ts"() {
5057
5257
  "use strict";
5258
+ init_table_width_model();
5058
5259
  DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR = "#ffffff";
5059
5260
  DEFAULT_HTML_TABLE_TEXT_COLOR = "#000000";
5060
5261
  BORDER_STYLES = /* @__PURE__ */ new Set([
@@ -5418,10 +5619,14 @@ function findTableNodeInfoFromState(state, anchorPos) {
5418
5619
  function applyTableAlignment(editor, tableAlign, anchorPos) {
5419
5620
  const tableInfo = findTableNodeInfoFromState(editor.state, anchorPos);
5420
5621
  if (!tableInfo) return false;
5622
+ const responsive = normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive";
5623
+ const widthBp = getResponsiveTableWidthBp(tableInfo.node);
5624
+ const offsetBp = resolveResponsiveTableOffsetBp(widthBp, tableAlign);
5421
5625
  editor.view.dispatch(
5422
5626
  editor.state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
5423
5627
  ...tableInfo.node.attrs,
5424
- textAlign: tableAlign
5628
+ textAlign: tableAlign,
5629
+ ...responsive ? { offsetBp } : null
5425
5630
  })
5426
5631
  );
5427
5632
  const domAtTable = editor.view.domAtPos(Math.min(tableInfo.pos + 1, editor.state.doc.content.size)).node;
@@ -5438,8 +5643,8 @@ function applyTableAlignment(editor, tableAlign, anchorPos) {
5438
5643
  }
5439
5644
  if (tableAlign) {
5440
5645
  tableElement.setAttribute("data-table-align", tableAlign);
5441
- tableElement.style.marginLeft = tableAlign === "center" || tableAlign === "right" ? "auto" : "0";
5442
- tableElement.style.marginRight = tableAlign === "center" ? "auto" : tableAlign === "right" ? "0" : "auto";
5646
+ tableElement.style.marginLeft = responsive ? formatBasisPointsAsPercentage(offsetBp) : tableAlign === "center" || tableAlign === "right" ? "auto" : "0";
5647
+ tableElement.style.marginRight = responsive ? "auto" : tableAlign === "center" ? "auto" : tableAlign === "right" ? "0" : "auto";
5443
5648
  } else {
5444
5649
  tableElement.removeAttribute("data-table-align");
5445
5650
  tableElement.style.removeProperty("margin-left");
@@ -5452,6 +5657,7 @@ var init_table_align_utils = __esm({
5452
5657
  "src/components/UEditor/table-align-utils.ts"() {
5453
5658
  "use strict";
5454
5659
  init_table_dom_utils();
5660
+ init_table_width_model();
5455
5661
  }
5456
5662
  });
5457
5663
 
@@ -5700,7 +5906,24 @@ function duplicateTableColumnAt(editor, columnIndex, cellPos) {
5700
5906
  cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
5701
5907
  return rowInfo.node.type.create(rowInfo.node.attrs, cells);
5702
5908
  });
5703
- return tableNode.type.create(tableNode.attrs, rows);
5909
+ const responsiveLayout = normalizeTableWidthMode(tableNode.attrs.widthMode) === "responsive" ? insertResponsiveColumnLayout(
5910
+ getResponsiveTableWidthBp(tableNode),
5911
+ getTableColumnRatios(tableNode),
5912
+ columnIndex + 1,
5913
+ columnIndex
5914
+ ) : null;
5915
+ const tableAttrs = responsiveLayout ? {
5916
+ ...tableNode.attrs,
5917
+ widthMode: "responsive",
5918
+ widthBp: responsiveLayout.widthBp,
5919
+ offsetBp: resolveResponsiveTableOffsetBp(
5920
+ responsiveLayout.widthBp,
5921
+ tableNode.attrs.textAlign,
5922
+ tableNode.attrs.offsetBp
5923
+ ),
5924
+ columnRatios: responsiveLayout.columnRatios
5925
+ } : tableNode.attrs;
5926
+ return tableNode.type.create(tableAttrs, rows);
5704
5927
  });
5705
5928
  }
5706
5929
  function clearTableColumnAt(editor, columnIndex, cellPos) {
@@ -5744,6 +5967,7 @@ var init_table_cell_commands = __esm({
5744
5967
  import_state9 = require("@tiptap/pm/state");
5745
5968
  import_tables4 = require("@tiptap/pm/tables");
5746
5969
  init_table_dom_utils();
5970
+ init_table_width_model();
5747
5971
  }
5748
5972
  });
5749
5973
 
@@ -8645,6 +8869,17 @@ function normalizePreviewRowHeight(row) {
8645
8869
  function normalizePreviewTable(table) {
8646
8870
  const widths = resolveColumnWidths(table);
8647
8871
  if (widths.length === 0) return;
8872
+ const storedMode = table.getAttribute("data-table-width-mode");
8873
+ const responsive = storedMode === "responsive" || storedMode === "full" || parsePercentageToBasisPoints(table.style.width) !== null;
8874
+ const storedRatios = parseColumnRatios(table.getAttribute("data-table-column-ratios"));
8875
+ const columnRatios = normalizeColumnRatios(
8876
+ storedRatios?.length === widths.length ? storedRatios : widths,
8877
+ widths.length
8878
+ );
8879
+ const widthBp = responsive ? Number(table.getAttribute("data-table-width-bp")) || parsePercentageToBasisPoints(table.getAttribute("data-table-width") ?? table.style.width) || TABLE_WIDTH_BASIS_POINTS : TABLE_WIDTH_BASIS_POINTS;
8880
+ const safeWidthBp = clampResponsiveTableWidthBp(widthBp);
8881
+ const offsetBp = responsive ? Number(table.getAttribute("data-table-offset-bp")) || parsePercentageToBasisPoints(table.getAttribute("data-table-offset") ?? table.style.marginLeft) || 0 : 0;
8882
+ const safeOffsetBp = resolveResponsiveTableOffsetBp(safeWidthBp, null, offsetBp);
8648
8883
  let colgroup = table.querySelector("colgroup");
8649
8884
  if (!colgroup) {
8650
8885
  colgroup = document.createElement("colgroup");
@@ -8653,6 +8888,7 @@ function normalizePreviewTable(table) {
8653
8888
  while (colgroup.children.length < widths.length) {
8654
8889
  colgroup.appendChild(document.createElement("col"));
8655
8890
  }
8891
+ const tableWidth = widths.reduce((sum, width) => sum + width, 0);
8656
8892
  Array.from(colgroup.children).forEach((child, index) => {
8657
8893
  if (child.tagName.toLowerCase() !== "col") return;
8658
8894
  const col = child;
@@ -8660,23 +8896,31 @@ function normalizePreviewTable(table) {
8660
8896
  child.remove();
8661
8897
  return;
8662
8898
  }
8663
- col.style.width = `${widths[index]}px`;
8664
- col.style.minWidth = `${widths[index]}px`;
8899
+ col.style.width = responsive ? formatBasisPointsAsPercentage(columnRatios[index]) : `${widths[index]}px`;
8900
+ col.style.minWidth = responsive ? "" : `${widths[index]}px`;
8665
8901
  col.setAttribute("width", String(widths[index]));
8666
8902
  });
8667
- const tableWidth = widths.reduce((sum, width) => sum + width, 0);
8668
- setStyleProperty(table, "width", `${tableWidth}px`);
8669
- setStyleProperty(table, "min-width", `${tableWidth}px`);
8903
+ setStyleProperty(table, "width", responsive ? formatBasisPointsAsPercentage(safeWidthBp) : `${tableWidth}px`);
8904
+ setStyleProperty(table, "min-width", responsive ? `${widths.length * TIPTAP_TABLE_MIN_COLUMN_WIDTH}px` : `${tableWidth}px`);
8670
8905
  setStyleProperty(table, "table-layout", "fixed");
8906
+ if (responsive) {
8907
+ table.setAttribute("data-table-width-mode", "responsive");
8908
+ table.setAttribute("data-table-width-bp", String(safeWidthBp));
8909
+ table.setAttribute("data-table-offset-bp", String(safeOffsetBp));
8910
+ table.setAttribute("data-table-column-ratios", columnRatios.join(","));
8911
+ setStyleProperty(table, "margin-left", formatBasisPointsAsPercentage(safeOffsetBp));
8912
+ setStyleProperty(table, "margin-right", "auto");
8913
+ }
8671
8914
  Array.from(table.rows).forEach((row) => {
8672
8915
  let columnIndex = 0;
8673
8916
  normalizePreviewRowHeight(row);
8674
8917
  Array.from(row.cells).forEach((cell) => {
8675
8918
  const colspan = getCellColspan(cell);
8676
8919
  const cellWidth = widths.slice(columnIndex, columnIndex + colspan).reduce((sum, width) => sum + width, 0);
8920
+ const cellRatio = columnRatios.slice(columnIndex, columnIndex + colspan).reduce((sum, ratio) => sum + ratio, 0);
8677
8921
  if (cellWidth > 0) {
8678
- cell.style.width = `${cellWidth}px`;
8679
- cell.style.minWidth = `${cellWidth}px`;
8922
+ cell.style.width = responsive ? formatBasisPointsAsPercentage(cellRatio) : `${cellWidth}px`;
8923
+ cell.style.minWidth = responsive ? "" : `${cellWidth}px`;
8680
8924
  }
8681
8925
  columnIndex += colspan;
8682
8926
  });
@@ -8694,6 +8938,7 @@ var init_preview_html = __esm({
8694
8938
  "src/components/UEditor/preview-html.ts"() {
8695
8939
  "use strict";
8696
8940
  init_table_dom_utils();
8941
+ init_table_width_model();
8697
8942
  DEFAULT_TABLE_COLUMN_WIDTH2 = 100;
8698
8943
  TIPTAP_TABLE_MIN_COLUMN_WIDTH = 25;
8699
8944
  }
@@ -12352,20 +12597,17 @@ var import_state6 = require("@tiptap/pm/state");
12352
12597
  var import_view2 = require("@tiptap/pm/view");
12353
12598
  var import_tables2 = require("@tiptap/pm/tables");
12354
12599
  init_table_dom_utils();
12600
+ init_table_width_model();
12355
12601
  var DEFAULT_TABLE_COLUMN_WIDTH = 100;
12356
12602
  var MIN_RESIZED_TABLE_COLUMN_WIDTH = 25;
12357
12603
  function getColumnResizeMinWidth(configuredMinWidth) {
12358
12604
  const normalizedMinWidth = Number.isFinite(configuredMinWidth) && configuredMinWidth > 0 ? Math.round(configuredMinWidth) : MIN_RESIZED_TABLE_COLUMN_WIDTH;
12359
12605
  return Math.max(MIN_RESIZED_TABLE_COLUMN_WIDTH, normalizedMinWidth);
12360
12606
  }
12361
- function setColumnStyle(column, width) {
12362
- if (width == null) {
12363
- column.style.width = `${DEFAULT_TABLE_COLUMN_WIDTH}px`;
12364
- column.style.minWidth = `${DEFAULT_TABLE_COLUMN_WIDTH}px`;
12365
- return;
12366
- }
12367
- column.style.width = `${Math.max(width, MIN_RESIZED_TABLE_COLUMN_WIDTH)}px`;
12368
- column.style.minWidth = "";
12607
+ function setColumnStyle(column, width, ratio, explicit, responsive) {
12608
+ column.style.width = responsive ? formatBasisPointsAsPercentage(ratio) : `${width}px`;
12609
+ column.style.minWidth = responsive || explicit ? "" : `${width}px`;
12610
+ column.setAttribute("width", String(width));
12369
12611
  }
12370
12612
  function isTableColumnElement(node) {
12371
12613
  return isCrossRealmElement(node) && String(node.tagName).toUpperCase() === "COL";
@@ -12374,6 +12616,7 @@ function updateDynamicColumns(node, colgroup, table, ownerDocument, overrideCol,
12374
12616
  let totalWidth = 0;
12375
12617
  let nextDOM = colgroup.firstChild;
12376
12618
  const row = node.firstChild;
12619
+ const columns = [];
12377
12620
  if (row) {
12378
12621
  for (let rowCellIndex = 0, col = 0; rowCellIndex < row.childCount; rowCellIndex += 1) {
12379
12622
  const { colspan, colwidth } = row.child(rowCellIndex).attrs;
@@ -12382,7 +12625,11 @@ function updateDynamicColumns(node, colgroup, table, ownerDocument, overrideCol,
12382
12625
  const width = rawWidth ? Math.max(rawWidth, MIN_RESIZED_TABLE_COLUMN_WIDTH) : null;
12383
12626
  totalWidth += width ?? DEFAULT_TABLE_COLUMN_WIDTH;
12384
12627
  const colElement = isTableColumnElement(nextDOM) ? nextDOM : colgroup.appendChild(ownerDocument.createElement("col"));
12385
- setColumnStyle(colElement, width);
12628
+ columns.push({
12629
+ element: colElement,
12630
+ explicit: width !== null,
12631
+ width: width ?? DEFAULT_TABLE_COLUMN_WIDTH
12632
+ });
12386
12633
  nextDOM = colElement.nextSibling;
12387
12634
  }
12388
12635
  }
@@ -12392,13 +12639,39 @@ function updateDynamicColumns(node, colgroup, table, ownerDocument, overrideCol,
12392
12639
  nextDOM.parentNode?.removeChild(nextDOM);
12393
12640
  nextDOM = after;
12394
12641
  }
12395
- const hasUserWidth = typeof node.attrs.style === "string" && /\bwidth\s*:/i.test(node.attrs.style);
12396
- if (!hasUserWidth) {
12642
+ const responsive = normalizeTableWidthMode(node.attrs.widthMode) === "responsive";
12643
+ const columnRatios = getTableColumnRatios(node);
12644
+ columns.forEach(({ element, explicit, width }, index) => {
12645
+ setColumnStyle(element, width, columnRatios[index] ?? 1, explicit, responsive);
12646
+ });
12647
+ if (responsive) {
12648
+ const widthBp = getResponsiveTableWidthBp(node);
12649
+ const offsetBp = getResponsiveTableOffsetBp(node);
12650
+ table.setAttribute("data-table-width-mode", "responsive");
12651
+ table.setAttribute("data-table-width", formatBasisPointsAsPercentage(widthBp));
12652
+ table.setAttribute("data-table-width-bp", String(widthBp));
12653
+ table.setAttribute("data-table-offset", formatBasisPointsAsPercentage(offsetBp));
12654
+ table.setAttribute("data-table-offset-bp", String(offsetBp));
12655
+ table.setAttribute("data-table-column-ratios", columnRatios.join(","));
12656
+ table.style.width = formatBasisPointsAsPercentage(widthBp);
12657
+ table.style.marginLeft = formatBasisPointsAsPercentage(offsetBp);
12658
+ table.style.marginRight = "auto";
12659
+ table.style.minWidth = `${Math.max(1, colgroup.childElementCount) * MIN_RESIZED_TABLE_COLUMN_WIDTH}px`;
12660
+ } else {
12661
+ table.removeAttribute("data-table-width-mode");
12662
+ table.removeAttribute("data-table-width");
12663
+ table.removeAttribute("data-table-width-bp");
12664
+ table.removeAttribute("data-table-offset");
12665
+ table.removeAttribute("data-table-offset-bp");
12666
+ table.removeAttribute("data-table-column-ratios");
12397
12667
  table.style.width = `${totalWidth}px`;
12398
12668
  table.style.minWidth = "";
12399
- } else {
12400
- table.style.minWidth = `${totalWidth}px`;
12669
+ const tableAlign = node.attrs.textAlign;
12670
+ table.style.marginLeft = tableAlign === "center" || tableAlign === "right" ? "auto" : "0px";
12671
+ table.style.marginRight = tableAlign === "center" ? "auto" : tableAlign === "right" ? "0px" : "auto";
12401
12672
  }
12673
+ if (node.attrs.textAlign) table.setAttribute("data-table-align", String(node.attrs.textAlign));
12674
+ else table.removeAttribute("data-table-align");
12402
12675
  }
12403
12676
  var UEditorTableView = class {
12404
12677
  constructor(node, _defaultColumnWidth, maybeView) {
@@ -12434,7 +12707,40 @@ var UEditorTableView = class {
12434
12707
  };
12435
12708
  function getDraggedWidth(dragging, event) {
12436
12709
  const offset = event.clientX - dragging.startX;
12437
- return Math.max(dragging.minWidth, Math.round(dragging.startWidth + offset));
12710
+ const maximum = dragging.neighborStartWidth === void 0 ? Number.POSITIVE_INFINITY : dragging.startWidth + dragging.neighborStartWidth - dragging.minWidth;
12711
+ return Math.min(maximum, Math.max(dragging.minWidth, Math.round(dragging.startWidth + offset)));
12712
+ }
12713
+ function normalizeColumnWidthsToTotal(values, total, minimum) {
12714
+ const safeTotal = Math.max(values.length * minimum, Math.round(total));
12715
+ const weights = values.map((value) => Number.isFinite(value) && value > 0 ? value : DEFAULT_TABLE_COLUMN_WIDTH);
12716
+ const weightSum = weights.reduce((sum, value) => sum + value, 0);
12717
+ const widths = weights.map((value) => Math.max(minimum, Math.round(value / weightSum * safeTotal)));
12718
+ let difference = safeTotal - widths.reduce((sum, value) => sum + value, 0);
12719
+ while (difference !== 0) {
12720
+ let changed = false;
12721
+ for (let index = widths.length - 1; index >= 0 && difference !== 0; index -= 1) {
12722
+ if (difference < 0 && widths[index] <= minimum) continue;
12723
+ widths[index] += difference > 0 ? 1 : -1;
12724
+ difference += difference > 0 ? -1 : 1;
12725
+ changed = true;
12726
+ }
12727
+ if (!changed) break;
12728
+ }
12729
+ return widths;
12730
+ }
12731
+ function getResizeColumnInfo(state, cell) {
12732
+ const $cell = state.doc.resolve(cell);
12733
+ const table = $cell.node(-1);
12734
+ const map = import_tables2.TableMap.get(table);
12735
+ const start = $cell.start(-1);
12736
+ const nodeAfter = $cell.nodeAfter;
12737
+ if (!nodeAfter) return null;
12738
+ return {
12739
+ col: map.colCount($cell.pos - start) + nodeAfter.attrs.colspan - 1,
12740
+ map,
12741
+ start,
12742
+ table
12743
+ };
12438
12744
  }
12439
12745
  function getCurrentColWidth(view, cellPos, { colspan, colwidth }) {
12440
12746
  const width = colwidth?.[colwidth.length - 1];
@@ -12503,6 +12809,12 @@ function handleMouseMove(view, event, handleWidth, lastColumnResizable) {
12503
12809
  if (event.clientX - left <= handleWidth) cell = edgeCell(view, event, "left", handleWidth);
12504
12810
  else if (right - event.clientX <= handleWidth) cell = edgeCell(view, event, "right", handleWidth);
12505
12811
  }
12812
+ if (cell !== -1) {
12813
+ const info = getResizeColumnInfo(view.state, cell);
12814
+ if (info && normalizeTableWidthMode(info.table.attrs.widthMode) === "responsive" && info.col === info.map.width - 1) {
12815
+ cell = -1;
12816
+ }
12817
+ }
12506
12818
  if (cell === pluginState.activeHandle) {
12507
12819
  clearHandleHoverTimer();
12508
12820
  return;
@@ -12549,33 +12861,47 @@ function handleMouseLeave(view) {
12549
12861
  updateHandle(view, -1);
12550
12862
  }
12551
12863
  }
12552
- function updateColumnWidth(view, cell, width) {
12553
- const $cell = view.state.doc.resolve(cell);
12554
- const table = $cell.node(-1);
12555
- const map = import_tables2.TableMap.get(table);
12556
- const start = $cell.start(-1);
12557
- const nodeAfter = $cell.nodeAfter;
12558
- if (!nodeAfter) return;
12559
- const col = map.colCount($cell.pos - start) + nodeAfter.attrs.colspan - 1;
12864
+ function updateColumnWidths(view, cell, widthsByColumn, columnRatios) {
12865
+ const info = getResizeColumnInfo(view.state, cell);
12866
+ if (!info) return;
12867
+ const { map, start, table } = info;
12560
12868
  const tr = view.state.tr;
12561
- for (let row = 0; row < map.height; row += 1) {
12562
- const mapIndex = row * map.width + col;
12563
- if (row && map.map[mapIndex] === map.map[mapIndex - map.width]) continue;
12564
- const pos = map.map[mapIndex];
12869
+ const seenCellPositions = /* @__PURE__ */ new Set();
12870
+ for (const pos of map.map) {
12871
+ if (seenCellPositions.has(pos)) continue;
12872
+ seenCellPositions.add(pos);
12565
12873
  const cellNode = table.nodeAt(pos);
12566
12874
  if (!cellNode) continue;
12567
12875
  const attrs = cellNode.attrs;
12568
- const index = attrs.colspan === 1 ? 0 : col - map.colCount(pos);
12569
- if (attrs.colwidth?.[index] === width) continue;
12570
12876
  const colwidth = attrs.colwidth ? attrs.colwidth.slice() : Array.from({ length: attrs.colspan }, () => 0);
12571
- colwidth[index] = width;
12877
+ const cellStartColumn = map.colCount(pos);
12878
+ let changed = false;
12879
+ for (let index = 0; index < attrs.colspan; index += 1) {
12880
+ const width = widthsByColumn.get(cellStartColumn + index);
12881
+ if (width === void 0 || colwidth[index] === width) continue;
12882
+ colwidth[index] = width;
12883
+ changed = true;
12884
+ }
12885
+ if (!changed) continue;
12572
12886
  tr.setNodeMarkup(start + pos, null, {
12573
12887
  ...attrs,
12574
12888
  colwidth
12575
12889
  });
12576
12890
  }
12891
+ if (columnRatios) {
12892
+ tr.setNodeMarkup(start - 1, void 0, {
12893
+ ...table.attrs,
12894
+ widthMode: "responsive",
12895
+ columnRatios: normalizeColumnRatios(columnRatios, map.width)
12896
+ });
12897
+ }
12577
12898
  if (tr.docChanged) view.dispatch(tr);
12578
12899
  }
12900
+ function updateColumnWidth(view, cell, width) {
12901
+ const info = getResizeColumnInfo(view.state, cell);
12902
+ if (!info) return;
12903
+ updateColumnWidths(view, cell, /* @__PURE__ */ new Map([[info.col, width]]));
12904
+ }
12579
12905
  function getActiveDragging(state) {
12580
12906
  const dragging = import_tables2.columnResizingPluginKey.getState(state)?.dragging;
12581
12907
  return dragging ? dragging : null;
@@ -12627,16 +12953,36 @@ function handleMouseDown(view, event, cellMinWidth) {
12627
12953
  if (!pluginState || pluginState.activeHandle === -1 || pluginState.dragging) return false;
12628
12954
  const cell = view.state.doc.nodeAt(pluginState.activeHandle);
12629
12955
  if (!cell) return false;
12956
+ const resizeInfo = getResizeColumnInfo(view.state, pluginState.activeHandle);
12957
+ if (!resizeInfo) return false;
12630
12958
  const attrs = cell.attrs;
12631
- const width = getCurrentColWidth(view, pluginState.activeHandle, {
12959
+ let width = getCurrentColWidth(view, pluginState.activeHandle, {
12632
12960
  colspan: attrs.colspan ?? 1,
12633
12961
  colwidth: attrs.colwidth
12634
12962
  });
12635
12963
  const minWidth = getColumnResizeMinWidth(cellMinWidth);
12964
+ let responsiveColumnWidths;
12965
+ let neighborStartWidth;
12966
+ if (normalizeTableWidthMode(resizeInfo.table.attrs.widthMode) === "responsive") {
12967
+ if (resizeInfo.col >= resizeInfo.map.width - 1) return false;
12968
+ const storedRatios = getTableColumnRatios(resizeInfo.table);
12969
+ const legacyWidths = getLegacyTableColumnWeights(resizeInfo.table, DEFAULT_TABLE_COLUMN_WIDTH);
12970
+ const storedTotal = legacyWidths.reduce((sum, value) => sum + value, 0);
12971
+ const tableElement = getTableElementAtCell(view, pluginState.activeHandle);
12972
+ const renderedWidth = tableElement?.getBoundingClientRect().width || storedTotal;
12973
+ responsiveColumnWidths = normalizeColumnWidthsToTotal(storedRatios, renderedWidth, minWidth);
12974
+ width = responsiveColumnWidths[resizeInfo.col];
12975
+ neighborStartWidth = responsiveColumnWidths[resizeInfo.col + 1];
12976
+ }
12636
12977
  const dragging = {
12637
12978
  startX: event.clientX,
12638
12979
  startWidth: width,
12639
- minWidth
12980
+ minWidth,
12981
+ ...responsiveColumnWidths ? {
12982
+ columnIndex: resizeInfo.col,
12983
+ responsiveColumnWidths,
12984
+ neighborStartWidth
12985
+ } : null
12640
12986
  };
12641
12987
  view.dispatch(view.state.tr.setMeta(import_tables2.columnResizingPluginKey, { setDragging: dragging }));
12642
12988
  function finish(nextEvent) {
@@ -12645,7 +12991,20 @@ function handleMouseDown(view, event, cellMinWidth) {
12645
12991
  const activeDragging = getActiveDragging(view.state);
12646
12992
  const activeHandle = import_tables2.columnResizingPluginKey.getState(view.state)?.activeHandle ?? -1;
12647
12993
  if (activeDragging && activeHandle > -1) {
12648
- updateColumnWidth(view, activeHandle, getDraggedWidth(activeDragging, nextEvent));
12994
+ const nextWidth = getDraggedWidth(activeDragging, nextEvent);
12995
+ if (activeDragging.responsiveColumnWidths && activeDragging.columnIndex !== void 0 && activeDragging.neighborStartWidth !== void 0) {
12996
+ const nextColumnWidths = activeDragging.responsiveColumnWidths.slice();
12997
+ nextColumnWidths[activeDragging.columnIndex] = nextWidth;
12998
+ nextColumnWidths[activeDragging.columnIndex + 1] = activeDragging.startWidth + activeDragging.neighborStartWidth - nextWidth;
12999
+ updateColumnWidths(
13000
+ view,
13001
+ activeHandle,
13002
+ new Map(nextColumnWidths.map((columnWidth, index) => [index, columnWidth])),
13003
+ normalizeColumnRatios(nextColumnWidths, nextColumnWidths.length)
13004
+ );
13005
+ } else {
13006
+ updateColumnWidth(view, activeHandle, nextWidth);
13007
+ }
12649
13008
  view.dispatch(view.state.tr.setMeta(import_tables2.columnResizingPluginKey, { setDragging: null }));
12650
13009
  }
12651
13010
  hideColumnResizeGhost(view);
@@ -12675,6 +13034,9 @@ function handleDecorations(state, cell, ownerDocument) {
12675
13034
  const nodeAfter = $cell.nodeAfter;
12676
13035
  if (!nodeAfter) return import_view2.DecorationSet.empty;
12677
13036
  const col = map.colCount($cell.pos - start) + nodeAfter.attrs.colspan - 1;
13037
+ if (normalizeTableWidthMode(table.attrs.widthMode) === "responsive" && col === map.width - 1) {
13038
+ return import_view2.DecorationSet.empty;
13039
+ }
12678
13040
  for (let row = 0; row < map.height; row += 1) {
12679
13041
  const index = col + row * map.width;
12680
13042
  if ((col === map.width - 1 || map.map[index] !== map.map[index + 1]) && (row === 0 || map.map[index] !== map.map[index - map.width])) {
@@ -12748,6 +13110,7 @@ function dynamicColumnResizing({
12748
13110
  // src/components/UEditor/table-align.ts
12749
13111
  init_table_align_utils();
12750
13112
  init_table_dom_utils();
13113
+ init_table_width_model();
12751
13114
  function normalizeTableAlign(value) {
12752
13115
  if (value === "left" || value === "center" || value === "right") {
12753
13116
  return value;
@@ -12764,7 +13127,7 @@ function parseTableAlign(element) {
12764
13127
  if ((marginLeft === "0px" || marginLeft === "0") && marginRight === "auto") return "left";
12765
13128
  return null;
12766
13129
  }
12767
- function renderTableAlignStyle(tableAlign) {
13130
+ function renderFixedTableAlignStyle(tableAlign) {
12768
13131
  switch (tableAlign) {
12769
13132
  case "center":
12770
13133
  return "table-layout: fixed; margin-left: auto; margin-right: auto;";
@@ -12776,28 +13139,61 @@ function renderTableAlignStyle(tableAlign) {
12776
13139
  return "";
12777
13140
  }
12778
13141
  }
12779
- function createUEditorColGroup(node) {
12780
- const columns = [];
12781
- let totalWidth = 0;
12782
- const firstRow = node.firstChild;
12783
- if (firstRow) {
12784
- for (let cellIndex = 0; cellIndex < firstRow.childCount; cellIndex += 1) {
12785
- const cell = firstRow.child(cellIndex);
12786
- const colspan = Math.max(1, Number(cell.attrs.colspan) || 1);
12787
- const colwidth = Array.isArray(cell.attrs.colwidth) ? cell.attrs.colwidth : [];
12788
- for (let spanIndex = 0; spanIndex < colspan; spanIndex += 1) {
12789
- const storedWidth = Number(colwidth[spanIndex]);
12790
- const width = Number.isFinite(storedWidth) && storedWidth > 0 ? Math.max(MIN_RESIZED_TABLE_COLUMN_WIDTH, Math.round(storedWidth)) : DEFAULT_TABLE_COLUMN_WIDTH;
12791
- totalWidth += width;
12792
- columns.push(["col", { style: `width: ${width}px; min-width: ${width}px;`, width: String(width) }]);
12793
- }
12794
- }
13142
+ function parseTableWidthMode(element) {
13143
+ const storedMode = element.getAttribute("data-table-width-mode");
13144
+ if (storedMode === "responsive" || storedMode === "full" || parsePercentageToBasisPoints(element.style.width) !== null) {
13145
+ return "responsive";
12795
13146
  }
13147
+ return "fixed";
13148
+ }
13149
+ function createUEditorColGroup(node, widthMode) {
13150
+ const columnWidths = getLegacyTableColumnWeights(node, DEFAULT_TABLE_COLUMN_WIDTH).map((width) => Math.max(MIN_RESIZED_TABLE_COLUMN_WIDTH, Math.round(width)));
13151
+ const columnRatios = getTableColumnRatios(node);
13152
+ const totalWidth = columnWidths.reduce((sum, width) => sum + width, 0);
13153
+ const columns = columnWidths.map((width, index) => ["col", {
13154
+ style: widthMode === "responsive" ? `width: ${formatBasisPointsAsPercentage(columnRatios[index])};` : `width: ${width}px; min-width: ${width}px;`,
13155
+ width: String(width)
13156
+ }]);
12796
13157
  return {
12797
13158
  colgroup: ["colgroup", {}, ...columns],
13159
+ columnCount: columns.length,
12798
13160
  tableWidth: totalWidth > 0 ? `${totalWidth}px` : ""
12799
13161
  };
12800
13162
  }
13163
+ function runResponsiveColumnStructureCommand(state, dispatch, command, updateLayout) {
13164
+ const tableInfo = findTableNodeInfoFromState(state);
13165
+ if (!tableInfo || normalizeTableWidthMode(tableInfo.node.attrs.widthMode) !== "responsive") {
13166
+ return command(state, dispatch);
13167
+ }
13168
+ const rect = (0, import_tables3.selectedRect)(state);
13169
+ const nextLayout = updateLayout({
13170
+ widthBp: getResponsiveTableWidthBp(tableInfo.node),
13171
+ columnRatios: getTableColumnRatios(tableInfo.node)
13172
+ }, rect);
13173
+ return command(state, dispatch ? (transaction) => {
13174
+ const nextTable = transaction.doc.nodeAt(tableInfo.pos);
13175
+ if (nextTable?.type.name === "table" && nextLayout.columnRatios.length > 0) {
13176
+ const widthBp = clampResponsiveTableWidthBp(nextLayout.widthBp);
13177
+ const tableAlign = normalizeTableAlign(nextTable.attrs.textAlign);
13178
+ const offsetBp = resolveResponsiveTableOffsetBp(
13179
+ widthBp,
13180
+ tableAlign,
13181
+ getResponsiveTableOffsetBp(tableInfo.node)
13182
+ );
13183
+ transaction.setNodeMarkup(tableInfo.pos, void 0, {
13184
+ ...nextTable.attrs,
13185
+ widthMode: "responsive",
13186
+ widthBp,
13187
+ offsetBp,
13188
+ columnRatios: normalizeColumnRatios(
13189
+ nextLayout.columnRatios,
13190
+ getLogicalTableColumnCount(nextTable)
13191
+ )
13192
+ });
13193
+ }
13194
+ dispatch(transaction);
13195
+ } : void 0);
13196
+ }
12801
13197
  var UEditorTable = import_extension_table.Table.extend({
12802
13198
  addGlobalAttributes() {
12803
13199
  return [
@@ -12815,10 +13211,64 @@ var UEditorTable = import_extension_table.Table.extend({
12815
13211
  const tableAlign = normalizeTableAlign(attributes.textAlign);
12816
13212
  if (!tableAlign) return {};
12817
13213
  return {
12818
- "data-table-align": tableAlign,
12819
- style: renderTableAlignStyle(tableAlign)
13214
+ "data-table-align": tableAlign
12820
13215
  };
12821
13216
  }
13217
+ },
13218
+ widthMode: {
13219
+ default: "fixed",
13220
+ parseHTML: (element) => {
13221
+ if (!(element instanceof HTMLElement)) return "fixed";
13222
+ return parseTableWidthMode(element);
13223
+ },
13224
+ renderHTML: (attributes) => {
13225
+ if (normalizeTableWidthMode(attributes.widthMode) !== "responsive") return {};
13226
+ return {
13227
+ "data-table-width-mode": "responsive"
13228
+ };
13229
+ }
13230
+ },
13231
+ widthBp: {
13232
+ default: null,
13233
+ parseHTML: (element) => {
13234
+ if (!(element instanceof HTMLElement) || parseTableWidthMode(element) !== "responsive") return null;
13235
+ const stored = Number(element.getAttribute("data-table-width-bp"));
13236
+ if (Number.isFinite(stored) && stored > 0) return clampResponsiveTableWidthBp(stored);
13237
+ return parsePercentageToBasisPoints(element.getAttribute("data-table-width") ?? element.style.width) ?? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP;
13238
+ },
13239
+ renderHTML: () => ({})
13240
+ },
13241
+ offsetBp: {
13242
+ default: null,
13243
+ parseHTML: (element) => {
13244
+ if (!(element instanceof HTMLElement) || parseTableWidthMode(element) !== "responsive") return null;
13245
+ const storedValue = element.getAttribute("data-table-offset-bp");
13246
+ const stored = Number(storedValue);
13247
+ if (storedValue !== null && Number.isFinite(stored) && stored >= 0) return Math.round(stored);
13248
+ const explicitOffset = parsePercentageToBasisPoints(
13249
+ element.getAttribute("data-table-offset") ?? element.style.marginLeft
13250
+ );
13251
+ if (explicitOffset !== null) return explicitOffset;
13252
+ const widthBp = parsePercentageToBasisPoints(
13253
+ element.getAttribute("data-table-width") ?? element.style.width
13254
+ ) ?? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP;
13255
+ const tableAlign = parseTableAlign(element);
13256
+ return resolveResponsiveTableOffsetBp(widthBp, tableAlign);
13257
+ },
13258
+ renderHTML: () => ({})
13259
+ },
13260
+ columnRatios: {
13261
+ default: null,
13262
+ parseHTML: (element) => {
13263
+ if (!(element instanceof HTMLElement)) return null;
13264
+ const storedRatios = parseColumnRatios(
13265
+ element.getAttribute("data-table-column-ratios") ?? element.getAttribute("data-column-ratios")
13266
+ );
13267
+ if (storedRatios) return storedRatios;
13268
+ const columnPercentages = Array.from(element.querySelectorAll("colgroup > col")).map((column) => parsePercentageToBasisPoints(column.style.width));
13269
+ return columnPercentages.length > 0 && columnPercentages.every((ratio) => ratio !== null) ? normalizeColumnRatios(columnPercentages, columnPercentages.length) : null;
13270
+ },
13271
+ renderHTML: () => ({})
12822
13272
  }
12823
13273
  }
12824
13274
  }
@@ -12827,13 +13277,86 @@ var UEditorTable = import_extension_table.Table.extend({
12827
13277
  addCommands() {
12828
13278
  return {
12829
13279
  ...this.parent?.(),
13280
+ insertTable: ({ rows = 3, cols = 3, withHeaderRow = true } = {}) => ({ tr, dispatch, editor }) => {
13281
+ const types = (0, import_tables3.tableNodeTypes)(editor.schema);
13282
+ const rowCount = Math.max(1, Math.floor(rows));
13283
+ const columnCount = Math.max(1, Math.floor(cols));
13284
+ const createCells = (type) => Array.from({ length: columnCount }, () => type.createAndFill()).filter((cell) => cell !== null);
13285
+ const bodyCells = createCells(types.cell);
13286
+ const headerCells = withHeaderRow ? createCells(types.header_cell) : bodyCells;
13287
+ const tableRows = Array.from({ length: rowCount }, (_, index) => types.row.createChecked(
13288
+ null,
13289
+ withHeaderRow && index === 0 ? headerCells : bodyCells
13290
+ ));
13291
+ const table = types.table.createChecked(
13292
+ {
13293
+ widthMode: "responsive",
13294
+ widthBp: DEFAULT_RESPONSIVE_TABLE_WIDTH_BP,
13295
+ offsetBp: 0,
13296
+ columnRatios: normalizeColumnRatios(null, columnCount)
13297
+ },
13298
+ tableRows
13299
+ );
13300
+ if (dispatch) {
13301
+ const offset = tr.selection.from + 1;
13302
+ tr.replaceSelectionWith(table).scrollIntoView().setSelection(import_state7.TextSelection.near(tr.doc.resolve(offset)));
13303
+ }
13304
+ return true;
13305
+ },
13306
+ addColumnBefore: () => ({ state, dispatch }) => {
13307
+ return runResponsiveColumnStructureCommand(
13308
+ state,
13309
+ dispatch,
13310
+ import_tables3.addColumnBefore,
13311
+ (layout, rect) => insertResponsiveColumnLayout(
13312
+ layout.widthBp,
13313
+ layout.columnRatios,
13314
+ rect.left,
13315
+ rect.left
13316
+ )
13317
+ );
13318
+ },
13319
+ addColumnAfter: () => ({ state, dispatch }) => {
13320
+ return runResponsiveColumnStructureCommand(
13321
+ state,
13322
+ dispatch,
13323
+ import_tables3.addColumnAfter,
13324
+ (layout, rect) => insertResponsiveColumnLayout(
13325
+ layout.widthBp,
13326
+ layout.columnRatios,
13327
+ rect.right,
13328
+ rect.right - 1
13329
+ )
13330
+ );
13331
+ },
13332
+ deleteColumn: () => ({ state, dispatch }) => {
13333
+ return runResponsiveColumnStructureCommand(
13334
+ state,
13335
+ dispatch,
13336
+ import_tables3.deleteColumn,
13337
+ (layout, rect) => {
13338
+ let nextLayout = layout;
13339
+ for (let index = rect.right - 1; index >= rect.left; index -= 1) {
13340
+ nextLayout = deleteResponsiveColumnLayout(
13341
+ nextLayout.widthBp,
13342
+ nextLayout.columnRatios,
13343
+ index
13344
+ );
13345
+ }
13346
+ return nextLayout;
13347
+ }
13348
+ );
13349
+ },
12830
13350
  setTableAlign: (tableAlign) => ({ state, dispatch }) => {
12831
13351
  const tableInfo = findTableNodeInfoFromState(state);
12832
13352
  if (!tableInfo) return false;
13353
+ const widthBp = getResponsiveTableWidthBp(tableInfo.node);
13354
+ const offsetBp = resolveResponsiveTableOffsetBp(widthBp, tableAlign);
12833
13355
  dispatch?.(
12834
13356
  state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
12835
13357
  ...tableInfo.node.attrs,
12836
- textAlign: tableAlign
13358
+ textAlign: tableAlign,
13359
+ ...normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive" ? { offsetBp } : null
12837
13360
  })
12838
13361
  );
12839
13362
  return true;
@@ -12852,11 +13375,31 @@ var UEditorTable = import_extension_table.Table.extend({
12852
13375
  };
12853
13376
  },
12854
13377
  renderHTML({ node, HTMLAttributes }) {
12855
- const { colgroup, tableWidth } = createUEditorColGroup(node);
13378
+ const widthMode = normalizeTableWidthMode(node.attrs.widthMode);
13379
+ const { colgroup, columnCount, tableWidth } = createUEditorColGroup(node, widthMode);
13380
+ const tableAlign = normalizeTableAlign(node.attrs.textAlign);
13381
+ const widthBp = getResponsiveTableWidthBp(node);
13382
+ const offsetBp = getResponsiveTableOffsetBp(node);
13383
+ const columnRatios = getTableColumnRatios(node);
13384
+ const tableStyle = widthMode === "responsive" ? [
13385
+ `width: ${formatBasisPointsAsPercentage(widthBp)}`,
13386
+ `margin-left: ${formatBasisPointsAsPercentage(offsetBp)}`,
13387
+ "margin-right: auto",
13388
+ `min-width: ${Math.max(1, columnCount) * MIN_RESIZED_TABLE_COLUMN_WIDTH}px`,
13389
+ "table-layout: fixed"
13390
+ ].join("; ") + ";" : tableWidth ? `width: ${tableWidth}; ${renderFixedTableAlignStyle(tableAlign)}` : `table-layout: fixed; ${renderFixedTableAlignStyle(tableAlign)}`;
12856
13391
  const table = [
12857
13392
  "table",
12858
13393
  (0, import_core17.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, {
12859
- style: tableWidth ? `width: ${tableWidth};` : "table-layout: fixed;"
13394
+ ...widthMode === "responsive" ? {
13395
+ "data-table-width-mode": "responsive",
13396
+ "data-table-width": formatBasisPointsAsPercentage(widthBp),
13397
+ "data-table-width-bp": String(widthBp),
13398
+ "data-table-offset": formatBasisPointsAsPercentage(offsetBp),
13399
+ "data-table-offset-bp": String(offsetBp),
13400
+ "data-table-column-ratios": columnRatios.join(",")
13401
+ } : null,
13402
+ style: tableStyle
12860
13403
  }),
12861
13404
  colgroup,
12862
13405
  ["tbody", 0]
@@ -12909,6 +13452,31 @@ var UEditorTable = import_extension_table.Table.extend({
12909
13452
  (0, import_tables3.tableEditing)({
12910
13453
  allowTableNodeSelection: this.options.allowTableNodeSelection
12911
13454
  }),
13455
+ new import_state7.Plugin({
13456
+ key: new import_state7.PluginKey("responsiveTableLayoutNormalizer"),
13457
+ appendTransaction(transactions, _oldState, newState) {
13458
+ if (!transactions.some((transaction) => transaction.docChanged)) return null;
13459
+ const tableInfo = findTableNodeInfoFromState(newState);
13460
+ const node = tableInfo?.node;
13461
+ if (!tableInfo || !node || normalizeTableWidthMode(node.attrs.widthMode) !== "responsive") return null;
13462
+ const columnCount = getLogicalTableColumnCount(node);
13463
+ const columnRatios = getTableColumnRatios(node);
13464
+ const widthBp = getResponsiveTableWidthBp(node);
13465
+ const offsetBp = getResponsiveTableOffsetBp(node);
13466
+ const storedRatios = parseColumnRatios(node.attrs.columnRatios);
13467
+ const ratiosMatch = storedRatios?.length === columnCount && storedRatios.every((ratio, index) => Math.round(ratio) === columnRatios[index]);
13468
+ if (ratiosMatch && node.attrs.widthBp === widthBp && node.attrs.offsetBp === offsetBp && node.attrs.widthMode === "responsive") {
13469
+ return null;
13470
+ }
13471
+ return newState.tr.setNodeMarkup(tableInfo.pos, void 0, {
13472
+ ...node.attrs,
13473
+ widthMode: "responsive",
13474
+ widthBp,
13475
+ offsetBp,
13476
+ columnRatios
13477
+ });
13478
+ }
13479
+ }),
12912
13480
  new import_state7.Plugin({
12913
13481
  appendTransaction(_transactions, _oldState, newState) {
12914
13482
  const { doc, schema } = newState;
@@ -19416,7 +19984,7 @@ var Selection = class {
19416
19984
  found.
19417
19985
  */
19418
19986
  static findFrom($pos, dir, textOnly = false) {
19419
- let inner = $pos.parent.inlineContent ? new TextSelection5($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
19987
+ let inner = $pos.parent.inlineContent ? new TextSelection6($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
19420
19988
  if (inner)
19421
19989
  return inner;
19422
19990
  for (let depth = $pos.depth - 1; depth >= 0; depth--) {
@@ -19485,7 +20053,7 @@ var Selection = class {
19485
20053
  returns the bookmark for that.
19486
20054
  */
19487
20055
  getBookmark() {
19488
- return TextSelection5.between(this.$anchor, this.$head).getBookmark();
20056
+ return TextSelection6.between(this.$anchor, this.$head).getBookmark();
19489
20057
  }
19490
20058
  };
19491
20059
  Selection.prototype.visible = true;
@@ -19505,7 +20073,7 @@ function checkTextSelection($pos) {
19505
20073
  console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")");
19506
20074
  }
19507
20075
  }
19508
- var TextSelection5 = class _TextSelection extends Selection {
20076
+ var TextSelection6 = class _TextSelection extends Selection {
19509
20077
  /**
19510
20078
  Construct a text selection between the given points.
19511
20079
  */
@@ -19591,7 +20159,7 @@ var TextSelection5 = class _TextSelection extends Selection {
19591
20159
  return new _TextSelection($anchor, $head);
19592
20160
  }
19593
20161
  };
19594
- Selection.jsonID("text", TextSelection5);
20162
+ Selection.jsonID("text", TextSelection6);
19595
20163
  var TextBookmark = class _TextBookmark {
19596
20164
  constructor(anchor, head) {
19597
20165
  this.anchor = anchor;
@@ -19601,7 +20169,7 @@ var TextBookmark = class _TextBookmark {
19601
20169
  return new _TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
19602
20170
  }
19603
20171
  resolve(doc) {
19604
- return TextSelection5.between(doc.resolve(this.anchor), doc.resolve(this.head));
20172
+ return TextSelection6.between(doc.resolve(this.anchor), doc.resolve(this.head));
19605
20173
  }
19606
20174
  };
19607
20175
  var NodeSelection2 = class _NodeSelection extends Selection {
@@ -19720,7 +20288,7 @@ var AllBookmark = {
19720
20288
  };
19721
20289
  function findSelectionIn(doc, node, pos, index, dir, text = false) {
19722
20290
  if (node.inlineContent)
19723
- return TextSelection5.create(doc, pos);
20291
+ return TextSelection6.create(doc, pos);
19724
20292
  for (let i = index - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
19725
20293
  let child = node.child(i);
19726
20294
  if (!child.isAtom) {
@@ -20220,7 +20788,7 @@ function freshColWidth(attrs) {
20220
20788
  for (let i = 0; i < attrs.colspan; i++) result.push(0);
20221
20789
  return result;
20222
20790
  }
20223
- function tableNodeTypes2(schema) {
20791
+ function tableNodeTypes3(schema) {
20224
20792
  let result = schema.cached.tableNodeTypes;
20225
20793
  if (!result) {
20226
20794
  result = schema.cached.tableNodeTypes = {};
@@ -20312,7 +20880,7 @@ var CellSelection2 = class CellSelection3 extends Selection {
20312
20880
  else if (tableChanged && this.isColSelection()) return CellSelection3.colSelection($anchorCell, $headCell);
20313
20881
  else return new CellSelection3($anchorCell, $headCell);
20314
20882
  }
20315
- return TextSelection5.between($anchorCell, $headCell);
20883
+ return TextSelection6.between($anchorCell, $headCell);
20316
20884
  }
20317
20885
  content() {
20318
20886
  const table = this.$anchorCell.node(-1);
@@ -20737,7 +21305,7 @@ function moveTableRow$1(table, indexesOrigin, indexesTarget, direction) {
20737
21305
  rows = moveRowInArrayOfRows(rows, indexesOrigin, indexesTarget, direction);
20738
21306
  return convertArrayOfRowsToTableNode(table, rows);
20739
21307
  }
20740
- function selectedRect4(state) {
21308
+ function selectedRect5(state) {
20741
21309
  const sel = state.selection;
20742
21310
  const $pos = selectionCell(state);
20743
21311
  const table = $pos.node(-1);
@@ -20754,8 +21322,8 @@ function deprecated_toggleHeader(type) {
20754
21322
  return function(state, dispatch) {
20755
21323
  if (!isInTable2(state)) return false;
20756
21324
  if (dispatch) {
20757
- const types = tableNodeTypes2(state.schema);
20758
- const rect = selectedRect4(state), tr = state.tr;
21325
+ const types = tableNodeTypes3(state.schema);
21326
+ const rect = selectedRect5(state), tr = state.tr;
20759
21327
  const cells = rect.map.cellsInRect(type == "column" ? {
20760
21328
  left: rect.left,
20761
21329
  top: 0,
@@ -20794,8 +21362,8 @@ function toggleHeader(type, options) {
20794
21362
  return function(state, dispatch) {
20795
21363
  if (!isInTable2(state)) return false;
20796
21364
  if (dispatch) {
20797
- const types = tableNodeTypes2(state.schema);
20798
- const rect = selectedRect4(state), tr = state.tr;
21365
+ const types = tableNodeTypes3(state.schema);
21366
+ const rect = selectedRect5(state), tr = state.tr;
20799
21367
  const isHeaderRowEnabled = isHeaderEnabledByType("row", rect, types);
20800
21368
  const isHeaderColumnEnabled = isHeaderEnabledByType("column", rect, types);
20801
21369
  const selectionStartsAt = (type === "column" ? isHeaderRowEnabled : type === "row" ? isHeaderColumnEnabled : false) ? 1 : 0;
@@ -20829,7 +21397,7 @@ function deleteCellSelection(state, dispatch) {
20829
21397
  if (!(sel instanceof CellSelection2)) return false;
20830
21398
  if (dispatch) {
20831
21399
  const tr = state.tr;
20832
- const baseContent = tableNodeTypes2(state.schema).cell.createAndFill().content;
21400
+ const baseContent = tableNodeTypes3(state.schema).cell.createAndFill().content;
20833
21401
  sel.forEachCell((cell, pos) => {
20834
21402
  if (!cell.content.eq(baseContent)) tr.replace(tr.mapping.map(pos + 1), tr.mapping.map(pos + cell.nodeSize - 1), new Slice(baseContent, 0, 0));
20835
21403
  });
@@ -20927,7 +21495,7 @@ function shiftArrow(axis, dir) {
20927
21495
  };
20928
21496
  }
20929
21497
  function atEndOfCell(view, axis, dir) {
20930
- if (!(view.state.selection instanceof TextSelection5)) return null;
21498
+ if (!(view.state.selection instanceof TextSelection6)) return null;
20931
21499
  const { $head } = view.state.selection;
20932
21500
  for (let d = $head.depth - 1; d >= 0; d--) {
20933
21501
  const parent = $head.node(d);
@@ -20954,6 +21522,7 @@ init_table_dom_utils();
20954
21522
 
20955
21523
  // src/components/UEditor/table-layout-model.ts
20956
21524
  init_table_dom_utils();
21525
+ init_table_width_model();
20957
21526
  var FALLBACK_TABLE_ROW_HEIGHT = DEFAULT_TABLE_ROW_HEIGHT;
20958
21527
  var FALLBACK_TABLE_COLUMN_WIDTH = 160;
20959
21528
  function isTableCellElement(element) {
@@ -21240,7 +21809,8 @@ function buildTableControlLayout(editor, surface, cell) {
21240
21809
  avgRowHeight,
21241
21810
  avgColumnWidth,
21242
21811
  rowHandles,
21243
- columnHandles
21812
+ columnHandles,
21813
+ widthMode: normalizeTableWidthMode(tableInfo.node.attrs.widthMode)
21244
21814
  };
21245
21815
  }
21246
21816
 
@@ -21738,11 +22308,17 @@ var HANDLE_BASE_CLASS = cn(
21738
22308
  "hover:bg-primary hover:text-primary-foreground hover:shadow-md active:scale-95",
21739
22309
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
21740
22310
  );
22311
+ var EDGE_HANDLE_CLASS = cn(
22312
+ "pointer-events-auto absolute top-0 z-10 h-full w-2 touch-none cursor-col-resize bg-transparent p-0",
22313
+ "border-transparent transition-colors duration-150 hover:border-primary",
22314
+ "focus-visible:border-primary focus-visible:outline-none"
22315
+ );
21741
22316
  function TableResizeHandles({
21742
22317
  active,
21743
22318
  ctrlHint,
21744
22319
  frameRef,
21745
22320
  layout,
22321
+ onFitWidth,
21746
22322
  onStartResize,
21747
22323
  resizeBothLabel
21748
22324
  }) {
@@ -21763,6 +22339,36 @@ function TableResizeHandles({
21763
22339
  height: layout.tableHeight
21764
22340
  },
21765
22341
  children: [
22342
+ layout.widthMode === "responsive" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
22343
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
22344
+ "button",
22345
+ {
22346
+ type: "button",
22347
+ "aria-label": `${resizeBothLabel} \u2014 \u2190`,
22348
+ title: resizeBothLabel,
22349
+ "data-table-resize-handle": "left",
22350
+ className: cn(
22351
+ EDGE_HANDLE_CLASS,
22352
+ "left-0 border-l-2"
22353
+ ),
22354
+ onPointerDown: (event) => onStartResize(event, "left")
22355
+ }
22356
+ ),
22357
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
22358
+ "button",
22359
+ {
22360
+ type: "button",
22361
+ "aria-label": `${resizeBothLabel} \u2014 \u2192`,
22362
+ title: resizeBothLabel,
22363
+ "data-table-resize-handle": "right",
22364
+ className: cn(
22365
+ EDGE_HANDLE_CLASS,
22366
+ "right-0 border-r-2"
22367
+ ),
22368
+ onPointerDown: (event) => onStartResize(event, "right")
22369
+ }
22370
+ )
22371
+ ] }) : null,
21766
22372
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21767
22373
  "button",
21768
22374
  {
@@ -21775,7 +22381,8 @@ function TableResizeHandles({
21775
22381
  "bottom-[-28px] right-[-30px] h-6 w-6 cursor-nwse-resize",
21776
22382
  active && "bg-primary text-primary-foreground shadow-md"
21777
22383
  ),
21778
- onPointerDown: onStartResize,
22384
+ onDoubleClick: onFitWidth,
22385
+ onPointerDown: (event) => onStartResize(event, "both"),
21779
22386
  children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.MoveDiagonal2, { "aria-hidden": "true", className: "h-3.5 w-3.5", strokeWidth: 2.25 })
21780
22387
  }
21781
22388
  ),
@@ -21796,6 +22403,7 @@ function TableResizeHandles({
21796
22403
 
21797
22404
  // src/components/UEditor/table-size-utils.ts
21798
22405
  init_table_dom_utils();
22406
+ init_table_width_model();
21799
22407
  var MAX_TABLE_DIMENSION = 8192;
21800
22408
  function positiveMetric(value, fallback) {
21801
22409
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
@@ -21838,7 +22446,7 @@ function getLogicalRowHeights(table, fallback) {
21838
22446
  });
21839
22447
  return heights;
21840
22448
  }
21841
- function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight) {
22449
+ function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight, containerWidth = startWidth) {
21842
22450
  const tableInfo = findTableInfo(editor, anchorPos);
21843
22451
  if (!tableInfo) return null;
21844
22452
  const tableMap = TableMap4.get(tableInfo.node);
@@ -21847,13 +22455,16 @@ function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight) {
21847
22455
  const safeHeight = Math.max(tableMap.height * MIN_TABLE_ROW_HEIGHT, Math.round(startHeight));
21848
22456
  const fallbackColumnWidth = safeWidth / tableMap.width;
21849
22457
  const fallbackRowHeight = safeHeight / tableMap.height;
22458
+ const widthMode = normalizeTableWidthMode(tableInfo.node.attrs.widthMode);
22459
+ const safeContainerWidth = Math.max(1, Math.round(containerWidth));
22460
+ const columnWeights = widthMode === "responsive" ? getTableColumnRatios(tableInfo.node) : getLogicalColumnWidths(tableInfo.node, tableMap, fallbackColumnWidth);
21850
22461
  return {
21851
22462
  anchorPos,
21852
22463
  tablePos: tableInfo.pos,
21853
22464
  startWidth: safeWidth,
21854
22465
  startHeight: safeHeight,
21855
22466
  columnWidths: normalizeWeightsToTotal(
21856
- getLogicalColumnWidths(tableInfo.node, tableMap, fallbackColumnWidth),
22467
+ columnWeights,
21857
22468
  safeWidth,
21858
22469
  MIN_RESIZED_TABLE_COLUMN_WIDTH
21859
22470
  ),
@@ -21863,7 +22474,12 @@ function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight) {
21863
22474
  MIN_TABLE_ROW_HEIGHT
21864
22475
  ),
21865
22476
  minWidth: tableMap.width * MIN_RESIZED_TABLE_COLUMN_WIDTH,
21866
- minHeight: tableMap.height * MIN_TABLE_ROW_HEIGHT
22477
+ minHeight: tableMap.height * MIN_TABLE_ROW_HEIGHT,
22478
+ containerWidth: safeContainerWidth,
22479
+ widthMode,
22480
+ widthBp: getResponsiveTableWidthBp(tableInfo.node),
22481
+ offsetBp: getResponsiveTableOffsetBp(tableInfo.node),
22482
+ columnRatios: getTableColumnRatios(tableInfo.node)
21867
22483
  };
21868
22484
  }
21869
22485
  function resolveTableResizeDimensions({
@@ -21871,12 +22487,14 @@ function resolveTableResizeDimensions({
21871
22487
  deltaY,
21872
22488
  lockAxis,
21873
22489
  preserveRatio,
22490
+ edge = "both",
21874
22491
  snapshot
21875
22492
  }) {
21876
- let width = snapshot.startWidth + deltaX;
21877
- let height = snapshot.startHeight + deltaY;
22493
+ const horizontalDelta = edge === "left" ? -deltaX : deltaX;
22494
+ let width = snapshot.startWidth + horizontalDelta;
22495
+ let height = edge === "both" ? snapshot.startHeight + deltaY : snapshot.startHeight;
21878
22496
  if (preserveRatio) {
21879
- const horizontalDrag = Math.abs(deltaX) >= Math.abs(deltaY);
22497
+ const horizontalDrag = edge !== "both" || Math.abs(deltaX) >= Math.abs(deltaY);
21880
22498
  const scale = horizontalDrag ? width / snapshot.startWidth : height / snapshot.startHeight;
21881
22499
  const minScale = Math.max(
21882
22500
  snapshot.minWidth / snapshot.startWidth,
@@ -21889,22 +22507,56 @@ function resolveTableResizeDimensions({
21889
22507
  const safeScale = Math.min(Math.max(scale, minScale), maxScale);
21890
22508
  width = snapshot.startWidth * safeScale;
21891
22509
  height = snapshot.startHeight * safeScale;
21892
- } else if (lockAxis) {
22510
+ } else if (lockAxis && edge === "both") {
21893
22511
  if (Math.abs(deltaX) >= Math.abs(deltaY)) {
21894
22512
  height = snapshot.startHeight;
21895
22513
  } else {
21896
22514
  width = snapshot.startWidth;
21897
22515
  }
21898
22516
  }
22517
+ let nextWidth = Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minWidth, Math.round(width)));
22518
+ const nextHeight = Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minHeight, Math.round(height)));
22519
+ if (snapshot.widthMode !== "responsive") {
22520
+ return { width: nextWidth, height: nextHeight };
22521
+ }
22522
+ const containerWidth = Math.max(1, snapshot.containerWidth);
22523
+ const startOffset = snapshot.offsetBp / TABLE_WIDTH_BASIS_POINTS * containerWidth;
22524
+ let nextOffset = startOffset;
22525
+ if (edge === "left") {
22526
+ const fixedRightEdge = startOffset + snapshot.startWidth;
22527
+ nextOffset = Math.min(
22528
+ fixedRightEdge - snapshot.minWidth,
22529
+ Math.max(0, startOffset + deltaX)
22530
+ );
22531
+ nextWidth = fixedRightEdge - nextOffset;
22532
+ }
22533
+ const widthBp = clampResponsiveTableWidthBp(
22534
+ nextWidth / containerWidth * TABLE_WIDTH_BASIS_POINTS
22535
+ );
22536
+ const offsetBp = resolveResponsiveTableOffsetBp(
22537
+ widthBp,
22538
+ null,
22539
+ nextOffset / containerWidth * TABLE_WIDTH_BASIS_POINTS
22540
+ );
21899
22541
  return {
21900
- width: Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minWidth, Math.round(width))),
21901
- height: Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minHeight, Math.round(height)))
22542
+ width: Math.round(widthBp / TABLE_WIDTH_BASIS_POINTS * containerWidth),
22543
+ height: nextHeight,
22544
+ widthBp,
22545
+ offsetBp,
22546
+ leftDelta: nextOffset - startOffset
21902
22547
  };
21903
22548
  }
21904
22549
  function arraysEqual(left, right) {
21905
22550
  return Array.isArray(left) && left.length === right.length && left.every((value, index) => value === right[index]);
21906
22551
  }
21907
- function applyTableSize(editor, snapshot, dimensions) {
22552
+ function getResponsiveResizeAlignment(widthBp, offsetBp) {
22553
+ const gap = Math.max(0, TABLE_WIDTH_BASIS_POINTS - widthBp);
22554
+ if (offsetBp === 0) return "left";
22555
+ if (offsetBp === gap) return "right";
22556
+ if (Math.abs(offsetBp * 2 - gap) <= 1) return "center";
22557
+ return null;
22558
+ }
22559
+ function applyTableSize(editor, snapshot, dimensions, options = {}) {
21908
22560
  const table = editor.state.doc.nodeAt(snapshot.tablePos);
21909
22561
  if (!table || table.type.name !== "table") return false;
21910
22562
  const tableMap = TableMap4.get(table);
@@ -21913,9 +22565,32 @@ function applyTableSize(editor, snapshot, dimensions) {
21913
22565
  }
21914
22566
  const resizeWidth = dimensions.width !== snapshot.startWidth;
21915
22567
  const resizeHeight = dimensions.height !== snapshot.startHeight;
21916
- if (!resizeWidth && !resizeHeight) return false;
22568
+ const widthMode = options.widthMode ?? snapshot.widthMode;
22569
+ const widthModeChanged = normalizeTableWidthMode(table.attrs.widthMode) !== widthMode || widthMode === "responsive" && table.attrs.widthMode !== "responsive";
22570
+ const widthBp = dimensions.widthBp ?? clampResponsiveTableWidthBp(
22571
+ dimensions.width / Math.max(1, snapshot.containerWidth) * TABLE_WIDTH_BASIS_POINTS
22572
+ );
22573
+ const offsetBp = resolveResponsiveTableOffsetBp(
22574
+ widthBp,
22575
+ null,
22576
+ dimensions.offsetBp ?? snapshot.offsetBp
22577
+ );
22578
+ const responsiveLayoutChanged = widthMode === "responsive" && (table.attrs.widthBp !== widthBp || table.attrs.offsetBp !== offsetBp);
22579
+ if (!resizeWidth && !resizeHeight && !widthModeChanged && !responsiveLayoutChanged) return false;
21917
22580
  const tableStart = snapshot.tablePos + 1;
21918
22581
  const transaction = editor.state.tr;
22582
+ if (widthModeChanged || responsiveLayoutChanged) {
22583
+ transaction.setNodeMarkup(snapshot.tablePos, void 0, {
22584
+ ...table.attrs,
22585
+ widthMode,
22586
+ ...widthMode === "responsive" ? {
22587
+ widthBp,
22588
+ offsetBp,
22589
+ columnRatios: normalizeColumnRatios(snapshot.columnRatios, tableMap.width),
22590
+ textAlign: getResponsiveResizeAlignment(widthBp, offsetBp)
22591
+ } : null
22592
+ });
22593
+ }
21919
22594
  if (resizeWidth) {
21920
22595
  const nextColumnWidths = normalizeWeightsToTotal(
21921
22596
  snapshot.columnWidths,
@@ -21958,6 +22633,7 @@ function applyTableSize(editor, snapshot, dimensions) {
21958
22633
  }
21959
22634
 
21960
22635
  // src/components/UEditor/table-controls.tsx
22636
+ init_table_width_model();
21961
22637
  var import_jsx_runtime34 = require("react/jsx-runtime");
21962
22638
  var TABLE_MENU_TOP_OFFSET = 10;
21963
22639
  var AXIS_HANDLE_RADIUS = 12;
@@ -22067,6 +22743,10 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
22067
22743
  if (!frame) return;
22068
22744
  frame.style.width = `${dimensions.width}px`;
22069
22745
  frame.style.height = `${dimensions.height}px`;
22746
+ const activeLayout = layoutRef.current;
22747
+ if (activeLayout) {
22748
+ frame.style.left = `${activeLayout.tableLeft + (dimensions.leftDelta ?? 0)}px`;
22749
+ }
22070
22750
  const dimensionsLabel = frame.querySelector("[data-table-resize-dimensions]");
22071
22751
  if (dimensionsLabel) {
22072
22752
  dimensionsLabel.textContent = `${dimensions.width} \xD7 ${dimensions.height}`;
@@ -22077,7 +22757,8 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
22077
22757
  if (!activeLayout) return;
22078
22758
  updateTableResizePreview({
22079
22759
  width: Math.round(activeLayout.tableWidth),
22080
- height: Math.round(activeLayout.tableHeight)
22760
+ height: Math.round(activeLayout.tableHeight),
22761
+ leftDelta: 0
22081
22762
  });
22082
22763
  }, [updateTableResizePreview]);
22083
22764
  const clearDrag = import_react37.default.useCallback(() => {
@@ -22242,7 +22923,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
22242
22923
  const canExpandTable = Boolean(layout);
22243
22924
  const controlsVisible = false;
22244
22925
  const tableMenuOpen = openMenuKey === "table";
22245
- const startTableResize = import_react37.default.useCallback((event) => {
22926
+ const startTableResize = import_react37.default.useCallback((event, edge) => {
22246
22927
  if (event.button !== 0 || dragStateRef.current) return;
22247
22928
  const activeLayout = layoutRef.current;
22248
22929
  if (!activeLayout) return;
@@ -22250,7 +22931,8 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
22250
22931
  editor,
22251
22932
  activeLayout.cellPos,
22252
22933
  activeLayout.tableWidth,
22253
- activeLayout.tableHeight
22934
+ activeLayout.tableHeight,
22935
+ activeLayout.viewportWidth
22254
22936
  );
22255
22937
  if (!snapshot) return;
22256
22938
  event.preventDefault();
@@ -22272,12 +22954,38 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
22272
22954
  pointerTarget: event.currentTarget,
22273
22955
  startX: event.clientX,
22274
22956
  startY: event.clientY,
22957
+ edge,
22275
22958
  snapshot,
22276
22959
  pendingDimensions
22277
22960
  };
22278
22961
  updateTableResizePreview(pendingDimensions);
22279
- setDocumentCursor(editorDocument, "nwse-resize");
22962
+ setDocumentCursor(editorDocument, edge === "both" ? "nwse-resize" : "ew-resize");
22280
22963
  }, [editor, editorDocument, updateTableResizePreview]);
22964
+ const fitTableToEditorWidth = import_react37.default.useCallback((event) => {
22965
+ if (event.button !== 0 || dragStateRef.current) return;
22966
+ const activeLayout = layoutRef.current;
22967
+ if (!activeLayout) return;
22968
+ const snapshot = createTableSizeSnapshot(
22969
+ editor,
22970
+ activeLayout.cellPos,
22971
+ activeLayout.tableWidth,
22972
+ activeLayout.tableHeight,
22973
+ activeLayout.viewportWidth
22974
+ );
22975
+ if (!snapshot) return;
22976
+ event.preventDefault();
22977
+ event.stopPropagation();
22978
+ const didResize = applyTableSize(editor, snapshot, {
22979
+ width: Math.round(activeLayout.viewportWidth),
22980
+ height: snapshot.startHeight,
22981
+ widthBp: TABLE_WIDTH_BASIS_POINTS,
22982
+ offsetBp: 0,
22983
+ leftDelta: -(snapshot.offsetBp / TABLE_WIDTH_BASIS_POINTS * snapshot.containerWidth)
22984
+ }, { widthMode: "responsive" });
22985
+ if (didResize) {
22986
+ scheduleSyncFromSelection();
22987
+ }
22988
+ }, [editor, scheduleSyncFromSelection]);
22281
22989
  const startAddColumnDrag = import_react37.default.useCallback(() => {
22282
22990
  setOpenMenuKey(null);
22283
22991
  dragStateRef.current = { kind: "add-column", previewCols: 1 };
@@ -22392,12 +23100,30 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
22392
23100
  scheduleSyncFromSelection();
22393
23101
  }
22394
23102
  if (dragState.kind === "column" && dragState.originIndex !== dragState.targetIndex) {
23103
+ const tableInfo = findTableInfo(editor, dragState.anchorPos);
23104
+ const responsiveRatios = tableInfo && normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive" ? moveResponsiveColumnRatio(
23105
+ getTableColumnRatios(tableInfo.node),
23106
+ dragState.originIndex,
23107
+ dragState.targetIndex
23108
+ ) : null;
22395
23109
  moveTableColumn({
22396
23110
  from: dragState.originIndex,
22397
23111
  to: dragState.targetIndex,
22398
23112
  pos: dragState.anchorPos,
22399
23113
  select: true
22400
- })(editor.state, editor.view.dispatch);
23114
+ })(editor.state, (transaction) => {
23115
+ if (tableInfo && responsiveRatios) {
23116
+ const nextTable = transaction.doc.nodeAt(tableInfo.pos);
23117
+ if (nextTable?.type.name === "table") {
23118
+ transaction.setNodeMarkup(tableInfo.pos, void 0, {
23119
+ ...nextTable.attrs,
23120
+ widthMode: "responsive",
23121
+ columnRatios: responsiveRatios
23122
+ });
23123
+ }
23124
+ }
23125
+ editor.view.dispatch(transaction);
23126
+ });
22401
23127
  scheduleSyncFromSelection();
22402
23128
  }
22403
23129
  if (dragState.kind === "add-row") {
@@ -22431,6 +23157,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
22431
23157
  deltaY: event.clientY - dragState.startY,
22432
23158
  lockAxis: event.ctrlKey && !event.shiftKey,
22433
23159
  preserveRatio: event.ctrlKey && event.shiftKey,
23160
+ edge: dragState.edge,
22434
23161
  snapshot: dragState.snapshot
22435
23162
  });
22436
23163
  if (tableResizePreviewFrameRef.current === null) {
@@ -22442,7 +23169,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
22442
23169
  updateTableResizePreview(currentDragState.pendingDimensions);
22443
23170
  });
22444
23171
  }
22445
- setDocumentCursor(editorDocument, "nwse-resize");
23172
+ setDocumentCursor(editorDocument, dragState.edge === "both" ? "nwse-resize" : "ew-resize");
22446
23173
  if (event.cancelable) event.preventDefault();
22447
23174
  };
22448
23175
  const finishTableResize = (event, commit) => {
@@ -22453,7 +23180,12 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
22453
23180
  tableResizePreviewFrameRef.current = null;
22454
23181
  }
22455
23182
  updateTableResizePreview(dragState.pendingDimensions);
22456
- if (commit && applyTableSize(editor, dragState.snapshot, dragState.pendingDimensions)) {
23183
+ if (commit && applyTableSize(
23184
+ editor,
23185
+ dragState.snapshot,
23186
+ dragState.pendingDimensions,
23187
+ { widthMode: dragState.snapshot.widthMode }
23188
+ )) {
22457
23189
  scheduleSyncFromSelection();
22458
23190
  }
22459
23191
  clearDrag();
@@ -22691,6 +23423,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
22691
23423
  ctrlHint: t("tableMenu.resizeCtrlHint"),
22692
23424
  frameRef: tableResizeFrameRef,
22693
23425
  layout,
23426
+ onFitWidth: fitTableToEditorWidth,
22694
23427
  onStartResize: startTableResize,
22695
23428
  resizeBothLabel: t("tableMenu.resizeBoth")
22696
23429
  }