@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/index.cjs CHANGED
@@ -5148,6 +5148,172 @@ var init_url_safety = __esm({
5148
5148
  }
5149
5149
  });
5150
5150
 
5151
+ // src/components/UEditor/table-width-model.ts
5152
+ function positiveNumber(value, fallback) {
5153
+ const number = Number(value);
5154
+ return Number.isFinite(number) && number > 0 ? number : fallback;
5155
+ }
5156
+ function clampTableBasisPoints(value, fallback = TABLE_WIDTH_BASIS_POINTS, maximum = TABLE_WIDTH_BASIS_POINTS) {
5157
+ const number = Number(value);
5158
+ if (!Number.isFinite(number)) return fallback;
5159
+ return Math.min(maximum, Math.max(0, Math.round(number)));
5160
+ }
5161
+ function clampResponsiveTableWidthBp(value, fallback = DEFAULT_RESPONSIVE_TABLE_WIDTH_BP) {
5162
+ return Math.max(1, clampTableBasisPoints(value, fallback, MAX_RESPONSIVE_TABLE_WIDTH_BP));
5163
+ }
5164
+ function normalizeTableWidthMode(value) {
5165
+ return value === "responsive" || value === "full" ? "responsive" : "fixed";
5166
+ }
5167
+ function parsePercentageToBasisPoints(value, maximum = MAX_RESPONSIVE_TABLE_WIDTH_BP) {
5168
+ if (!value) return null;
5169
+ const match = value.trim().match(/^(-?\d+(?:\.\d+)?)%$/);
5170
+ if (!match) return null;
5171
+ const percentage = Number.parseFloat(match[1]);
5172
+ if (!Number.isFinite(percentage)) return null;
5173
+ return clampTableBasisPoints(percentage * 100, TABLE_WIDTH_BASIS_POINTS, maximum);
5174
+ }
5175
+ function formatBasisPointsAsPercentage(value) {
5176
+ const percentage = clampTableBasisPoints(value, 0, MAX_RESPONSIVE_TABLE_WIDTH_BP) / 100;
5177
+ return `${Number.parseFloat(percentage.toFixed(2))}%`;
5178
+ }
5179
+ function parseColumnRatios(value) {
5180
+ const parts = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
5181
+ const ratios = parts.map((part) => Number(part));
5182
+ return ratios.length > 0 && ratios.every((ratio) => Number.isFinite(ratio) && ratio > 0) ? ratios : null;
5183
+ }
5184
+ function normalizeColumnRatios(values, columnCount) {
5185
+ if (columnCount <= 0) return [];
5186
+ const source = values?.length === columnCount ? values.map((value) => positiveNumber(value, 1)) : Array.from({ length: columnCount }, () => 1);
5187
+ const sourceTotal = source.reduce((sum, value) => sum + value, 0);
5188
+ const normalized = source.map((value) => Math.max(1, Math.round(value / sourceTotal * TABLE_WIDTH_BASIS_POINTS)));
5189
+ let difference = TABLE_WIDTH_BASIS_POINTS - normalized.reduce((sum, value) => sum + value, 0);
5190
+ while (difference !== 0) {
5191
+ let changed = false;
5192
+ for (let index = normalized.length - 1; index >= 0 && difference !== 0; index -= 1) {
5193
+ if (difference < 0 && normalized[index] <= 1) continue;
5194
+ normalized[index] += difference > 0 ? 1 : -1;
5195
+ difference += difference > 0 ? -1 : 1;
5196
+ changed = true;
5197
+ }
5198
+ if (!changed) break;
5199
+ }
5200
+ return normalized;
5201
+ }
5202
+ function getLogicalTableColumnCount(table) {
5203
+ const firstRow = table.firstChild;
5204
+ if (!firstRow) return 0;
5205
+ let count = 0;
5206
+ firstRow.forEach((cell) => {
5207
+ count += Math.max(1, Number(cell.attrs.colspan) || 1);
5208
+ });
5209
+ return count;
5210
+ }
5211
+ function getLegacyTableColumnWeights(table, fallback = 100) {
5212
+ const weights = [];
5213
+ const firstRow = table.firstChild;
5214
+ if (!firstRow) return weights;
5215
+ firstRow.forEach((cell) => {
5216
+ const colspan = Math.max(1, Number(cell.attrs.colspan) || 1);
5217
+ const colwidth = Array.isArray(cell.attrs.colwidth) ? cell.attrs.colwidth : [];
5218
+ for (let index = 0; index < colspan; index += 1) {
5219
+ weights.push(positiveNumber(colwidth[index], fallback));
5220
+ }
5221
+ });
5222
+ return weights;
5223
+ }
5224
+ function getTableColumnRatios(table) {
5225
+ const columnCount = getLogicalTableColumnCount(table);
5226
+ const stored = parseColumnRatios(table.attrs.columnRatios);
5227
+ return normalizeColumnRatios(
5228
+ stored?.length === columnCount ? stored : getLegacyTableColumnWeights(table),
5229
+ columnCount
5230
+ );
5231
+ }
5232
+ function getResponsiveTableWidthBp(table) {
5233
+ return clampResponsiveTableWidthBp(table.attrs.widthBp);
5234
+ }
5235
+ function getResponsiveTableOffsetBp(table) {
5236
+ const width = getResponsiveTableWidthBp(table);
5237
+ return resolveResponsiveTableOffsetBp(width, null, table.attrs.offsetBp);
5238
+ }
5239
+ function resolveResponsiveTableOffsetBp(widthBp, tableAlign, fallbackOffsetBp = 0) {
5240
+ const availableGap = Math.max(
5241
+ 0,
5242
+ TABLE_WIDTH_BASIS_POINTS - clampResponsiveTableWidthBp(widthBp)
5243
+ );
5244
+ if (tableAlign === "center") return Math.round(availableGap / 2);
5245
+ if (tableAlign === "right") return availableGap;
5246
+ if (tableAlign === "left") return 0;
5247
+ return Math.min(availableGap, clampTableBasisPoints(fallbackOffsetBp, 0));
5248
+ }
5249
+ function insertResponsiveColumnRatio(ratios, insertIndex, sourceIndex) {
5250
+ if (ratios.length === 0) return [TABLE_WIDTH_BASIS_POINTS];
5251
+ const safeSourceIndex = Math.max(0, Math.min(sourceIndex, ratios.length - 1));
5252
+ const next = [...ratios];
5253
+ const sourceRatio = next[safeSourceIndex];
5254
+ next.splice(Math.max(0, Math.min(insertIndex, next.length)), 0, sourceRatio);
5255
+ return normalizeColumnRatios(next, next.length);
5256
+ }
5257
+ function deleteResponsiveColumnRatio(ratios, deleteIndex) {
5258
+ if (ratios.length <= 1) return [];
5259
+ const next = ratios.filter((_, index) => index !== deleteIndex);
5260
+ return normalizeColumnRatios(next, next.length);
5261
+ }
5262
+ function moveResponsiveColumnRatio(ratios, from, to) {
5263
+ if (from === to || from < 0 || from >= ratios.length || to < 0 || to >= ratios.length) return [...ratios];
5264
+ const next = [...ratios];
5265
+ const [moved] = next.splice(from, 1);
5266
+ next.splice(to, 0, moved);
5267
+ return normalizeColumnRatios(next, next.length);
5268
+ }
5269
+ function insertResponsiveColumnLayout(widthBp, ratios, insertIndex, sourceIndex) {
5270
+ if (ratios.length === 0) {
5271
+ return {
5272
+ widthBp: clampResponsiveTableWidthBp(widthBp),
5273
+ columnRatios: [TABLE_WIDTH_BASIS_POINTS]
5274
+ };
5275
+ }
5276
+ const normalized = normalizeColumnRatios(ratios, ratios.length);
5277
+ const safeSourceIndex = Math.max(0, Math.min(sourceIndex, normalized.length - 1));
5278
+ const sourceRatio = normalized[safeSourceIndex];
5279
+ return {
5280
+ // Growing by the source column's share preserves every existing column's
5281
+ // rendered width instead of squeezing the whole table back into 100%.
5282
+ widthBp: clampResponsiveTableWidthBp(
5283
+ clampResponsiveTableWidthBp(widthBp) * (TABLE_WIDTH_BASIS_POINTS + sourceRatio) / TABLE_WIDTH_BASIS_POINTS
5284
+ ),
5285
+ columnRatios: insertResponsiveColumnRatio(normalized, insertIndex, safeSourceIndex)
5286
+ };
5287
+ }
5288
+ function deleteResponsiveColumnLayout(widthBp, ratios, deleteIndex) {
5289
+ const normalized = normalizeColumnRatios(ratios, ratios.length);
5290
+ if (normalized.length <= 1 || deleteIndex < 0 || deleteIndex >= normalized.length) {
5291
+ return {
5292
+ widthBp: clampResponsiveTableWidthBp(widthBp),
5293
+ columnRatios: normalized.length <= 1 ? [] : normalized
5294
+ };
5295
+ }
5296
+ const remainingRatio = TABLE_WIDTH_BASIS_POINTS - normalized[deleteIndex];
5297
+ const nextWidthBp = clampResponsiveTableWidthBp(
5298
+ clampResponsiveTableWidthBp(widthBp) * remainingRatio / TABLE_WIDTH_BASIS_POINTS
5299
+ );
5300
+ return {
5301
+ // Deletion is the inverse operation: remaining columns keep their rendered
5302
+ // widths while the table gives the removed column's space back.
5303
+ widthBp: Math.abs(nextWidthBp - DEFAULT_RESPONSIVE_TABLE_WIDTH_BP) <= 1 ? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP : nextWidthBp,
5304
+ columnRatios: deleteResponsiveColumnRatio(normalized, deleteIndex)
5305
+ };
5306
+ }
5307
+ var TABLE_WIDTH_BASIS_POINTS, DEFAULT_RESPONSIVE_TABLE_WIDTH_BP, MAX_RESPONSIVE_TABLE_WIDTH_BP;
5308
+ var init_table_width_model = __esm({
5309
+ "src/components/UEditor/table-width-model.ts"() {
5310
+ "use strict";
5311
+ TABLE_WIDTH_BASIS_POINTS = 1e4;
5312
+ DEFAULT_RESPONSIVE_TABLE_WIDTH_BP = TABLE_WIDTH_BASIS_POINTS;
5313
+ MAX_RESPONSIVE_TABLE_WIDTH_BP = 1e5;
5314
+ }
5315
+ });
5316
+
5151
5317
  // src/components/UEditor/clipboard-tables.ts
5152
5318
  function getClipboardData(dataTransfer, type) {
5153
5319
  try {
@@ -5764,13 +5930,34 @@ function createNormalizedRowContent(row, columnCount, fillerCellAttrs) {
5764
5930
  }
5765
5931
  return content;
5766
5932
  }
5767
- function createTableContent(rows, minColumnCount = 1, fillerCellAttrs) {
5933
+ function createTableContent(rows, minColumnCount = 1, fillerCellAttrs, layout) {
5768
5934
  const tableRows = rows.filter((row) => row.cells.length > 0);
5769
5935
  if (tableRows.length === 0) return null;
5770
5936
  const { positionedRows, columnCount } = normalizeTableRows(tableRows);
5771
5937
  if (columnCount < minColumnCount) return null;
5938
+ const inferredColumnWeights = Array.from({ length: columnCount }, () => 100);
5939
+ positionedRows.forEach((row) => {
5940
+ row.cells.forEach(({ cell, colspan, startColumn }) => {
5941
+ const width = colspan === 1 ? cell.attrs?.colwidth?.[0] : null;
5942
+ if (typeof width === "number" && Number.isFinite(width) && width > 0) {
5943
+ inferredColumnWeights[startColumn] = width;
5944
+ }
5945
+ });
5946
+ });
5947
+ const columnRatios = normalizeColumnRatios(
5948
+ layout?.columnRatios?.length === columnCount ? layout.columnRatios : inferredColumnWeights,
5949
+ columnCount
5950
+ );
5951
+ const widthBp = clampResponsiveTableWidthBp(layout?.widthBp, DEFAULT_RESPONSIVE_TABLE_WIDTH_BP);
5952
+ const offsetBp = resolveResponsiveTableOffsetBp(widthBp, null, layout?.offsetBp);
5772
5953
  return {
5773
5954
  type: "table",
5955
+ attrs: {
5956
+ widthMode: "responsive",
5957
+ widthBp,
5958
+ offsetBp,
5959
+ columnRatios
5960
+ },
5774
5961
  content: positionedRows.map((row) => ({
5775
5962
  type: "tableRow",
5776
5963
  ...row.attrs ? { attrs: row.attrs } : {},
@@ -5791,9 +5978,22 @@ function getClipboardTableContent(dataTransfer) {
5791
5978
  if (tables.length !== 1 || hasMeaningfulContentOutsideTable(sourceBody)) return null;
5792
5979
  const table = tables[0];
5793
5980
  if (!(table instanceof HTMLTableElement)) return null;
5794
- return createTableContent(getHtmlTableRows(table, styleMap), 1, {
5795
- backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
5796
- });
5981
+ const storedWidthValue = table.getAttribute("data-table-width-bp");
5982
+ const storedOffsetValue = table.getAttribute("data-table-offset-bp");
5983
+ const storedWidthBp = Number(storedWidthValue);
5984
+ const storedOffsetBp = Number(storedOffsetValue);
5985
+ const widthBp = storedWidthValue !== null && Number.isFinite(storedWidthBp) && storedWidthBp > 0 ? storedWidthBp : parsePercentageToBasisPoints(table.getAttribute("data-table-width") ?? table.style.width) ?? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP;
5986
+ const offsetBp = storedOffsetValue !== null && Number.isFinite(storedOffsetBp) && storedOffsetBp >= 0 ? storedOffsetBp : parsePercentageToBasisPoints(table.getAttribute("data-table-offset") ?? table.style.marginLeft) ?? 0;
5987
+ return createTableContent(
5988
+ getHtmlTableRows(table, styleMap),
5989
+ 1,
5990
+ { backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR },
5991
+ {
5992
+ widthBp,
5993
+ offsetBp,
5994
+ columnRatios: parseColumnRatios(table.getAttribute("data-table-column-ratios"))
5995
+ }
5996
+ );
5797
5997
  }
5798
5998
  function parseClipboardTsvRows(text) {
5799
5999
  const rows = [];
@@ -5860,6 +6060,7 @@ var DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR, DEFAULT_HTML_TABLE_TEXT_COLOR, BOR
5860
6060
  var init_clipboard_tables = __esm({
5861
6061
  "src/components/UEditor/clipboard-tables.ts"() {
5862
6062
  "use strict";
6063
+ init_table_width_model();
5863
6064
  DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR = "#ffffff";
5864
6065
  DEFAULT_HTML_TABLE_TEXT_COLOR = "#000000";
5865
6066
  BORDER_STYLES = /* @__PURE__ */ new Set([
@@ -6223,10 +6424,14 @@ function findTableNodeInfoFromState(state, anchorPos) {
6223
6424
  function applyTableAlignment(editor, tableAlign, anchorPos) {
6224
6425
  const tableInfo = findTableNodeInfoFromState(editor.state, anchorPos);
6225
6426
  if (!tableInfo) return false;
6427
+ const responsive = normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive";
6428
+ const widthBp = getResponsiveTableWidthBp(tableInfo.node);
6429
+ const offsetBp = resolveResponsiveTableOffsetBp(widthBp, tableAlign);
6226
6430
  editor.view.dispatch(
6227
6431
  editor.state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
6228
6432
  ...tableInfo.node.attrs,
6229
- textAlign: tableAlign
6433
+ textAlign: tableAlign,
6434
+ ...responsive ? { offsetBp } : null
6230
6435
  })
6231
6436
  );
6232
6437
  const domAtTable = editor.view.domAtPos(Math.min(tableInfo.pos + 1, editor.state.doc.content.size)).node;
@@ -6243,8 +6448,8 @@ function applyTableAlignment(editor, tableAlign, anchorPos) {
6243
6448
  }
6244
6449
  if (tableAlign) {
6245
6450
  tableElement.setAttribute("data-table-align", tableAlign);
6246
- tableElement.style.marginLeft = tableAlign === "center" || tableAlign === "right" ? "auto" : "0";
6247
- tableElement.style.marginRight = tableAlign === "center" ? "auto" : tableAlign === "right" ? "0" : "auto";
6451
+ tableElement.style.marginLeft = responsive ? formatBasisPointsAsPercentage(offsetBp) : tableAlign === "center" || tableAlign === "right" ? "auto" : "0";
6452
+ tableElement.style.marginRight = responsive ? "auto" : tableAlign === "center" ? "auto" : tableAlign === "right" ? "0" : "auto";
6248
6453
  } else {
6249
6454
  tableElement.removeAttribute("data-table-align");
6250
6455
  tableElement.style.removeProperty("margin-left");
@@ -6257,6 +6462,7 @@ var init_table_align_utils = __esm({
6257
6462
  "src/components/UEditor/table-align-utils.ts"() {
6258
6463
  "use strict";
6259
6464
  init_table_dom_utils();
6465
+ init_table_width_model();
6260
6466
  }
6261
6467
  });
6262
6468
 
@@ -6505,7 +6711,24 @@ function duplicateTableColumnAt(editor, columnIndex, cellPos) {
6505
6711
  cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
6506
6712
  return rowInfo.node.type.create(rowInfo.node.attrs, cells);
6507
6713
  });
6508
- return tableNode.type.create(tableNode.attrs, rows);
6714
+ const responsiveLayout = normalizeTableWidthMode(tableNode.attrs.widthMode) === "responsive" ? insertResponsiveColumnLayout(
6715
+ getResponsiveTableWidthBp(tableNode),
6716
+ getTableColumnRatios(tableNode),
6717
+ columnIndex + 1,
6718
+ columnIndex
6719
+ ) : null;
6720
+ const tableAttrs = responsiveLayout ? {
6721
+ ...tableNode.attrs,
6722
+ widthMode: "responsive",
6723
+ widthBp: responsiveLayout.widthBp,
6724
+ offsetBp: resolveResponsiveTableOffsetBp(
6725
+ responsiveLayout.widthBp,
6726
+ tableNode.attrs.textAlign,
6727
+ tableNode.attrs.offsetBp
6728
+ ),
6729
+ columnRatios: responsiveLayout.columnRatios
6730
+ } : tableNode.attrs;
6731
+ return tableNode.type.create(tableAttrs, rows);
6509
6732
  });
6510
6733
  }
6511
6734
  function clearTableColumnAt(editor, columnIndex, cellPos) {
@@ -6549,6 +6772,7 @@ var init_table_cell_commands = __esm({
6549
6772
  import_state9 = require("@tiptap/pm/state");
6550
6773
  import_tables4 = require("@tiptap/pm/tables");
6551
6774
  init_table_dom_utils();
6775
+ init_table_width_model();
6552
6776
  }
6553
6777
  });
6554
6778
 
@@ -8876,6 +9100,17 @@ function normalizePreviewRowHeight(row) {
8876
9100
  function normalizePreviewTable(table) {
8877
9101
  const widths = resolveColumnWidths(table);
8878
9102
  if (widths.length === 0) return;
9103
+ const storedMode = table.getAttribute("data-table-width-mode");
9104
+ const responsive = storedMode === "responsive" || storedMode === "full" || parsePercentageToBasisPoints(table.style.width) !== null;
9105
+ const storedRatios = parseColumnRatios(table.getAttribute("data-table-column-ratios"));
9106
+ const columnRatios = normalizeColumnRatios(
9107
+ storedRatios?.length === widths.length ? storedRatios : widths,
9108
+ widths.length
9109
+ );
9110
+ 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;
9111
+ const safeWidthBp = clampResponsiveTableWidthBp(widthBp);
9112
+ const offsetBp = responsive ? Number(table.getAttribute("data-table-offset-bp")) || parsePercentageToBasisPoints(table.getAttribute("data-table-offset") ?? table.style.marginLeft) || 0 : 0;
9113
+ const safeOffsetBp = resolveResponsiveTableOffsetBp(safeWidthBp, null, offsetBp);
8879
9114
  let colgroup = table.querySelector("colgroup");
8880
9115
  if (!colgroup) {
8881
9116
  colgroup = document.createElement("colgroup");
@@ -8884,6 +9119,7 @@ function normalizePreviewTable(table) {
8884
9119
  while (colgroup.children.length < widths.length) {
8885
9120
  colgroup.appendChild(document.createElement("col"));
8886
9121
  }
9122
+ const tableWidth = widths.reduce((sum, width) => sum + width, 0);
8887
9123
  Array.from(colgroup.children).forEach((child, index) => {
8888
9124
  if (child.tagName.toLowerCase() !== "col") return;
8889
9125
  const col = child;
@@ -8891,23 +9127,31 @@ function normalizePreviewTable(table) {
8891
9127
  child.remove();
8892
9128
  return;
8893
9129
  }
8894
- col.style.width = `${widths[index]}px`;
8895
- col.style.minWidth = `${widths[index]}px`;
9130
+ col.style.width = responsive ? formatBasisPointsAsPercentage(columnRatios[index]) : `${widths[index]}px`;
9131
+ col.style.minWidth = responsive ? "" : `${widths[index]}px`;
8896
9132
  col.setAttribute("width", String(widths[index]));
8897
9133
  });
8898
- const tableWidth = widths.reduce((sum, width) => sum + width, 0);
8899
- setStyleProperty(table, "width", `${tableWidth}px`);
8900
- setStyleProperty(table, "min-width", `${tableWidth}px`);
9134
+ setStyleProperty(table, "width", responsive ? formatBasisPointsAsPercentage(safeWidthBp) : `${tableWidth}px`);
9135
+ setStyleProperty(table, "min-width", responsive ? `${widths.length * TIPTAP_TABLE_MIN_COLUMN_WIDTH}px` : `${tableWidth}px`);
8901
9136
  setStyleProperty(table, "table-layout", "fixed");
9137
+ if (responsive) {
9138
+ table.setAttribute("data-table-width-mode", "responsive");
9139
+ table.setAttribute("data-table-width-bp", String(safeWidthBp));
9140
+ table.setAttribute("data-table-offset-bp", String(safeOffsetBp));
9141
+ table.setAttribute("data-table-column-ratios", columnRatios.join(","));
9142
+ setStyleProperty(table, "margin-left", formatBasisPointsAsPercentage(safeOffsetBp));
9143
+ setStyleProperty(table, "margin-right", "auto");
9144
+ }
8902
9145
  Array.from(table.rows).forEach((row) => {
8903
9146
  let columnIndex = 0;
8904
9147
  normalizePreviewRowHeight(row);
8905
9148
  Array.from(row.cells).forEach((cell) => {
8906
9149
  const colspan = getCellColspan(cell);
8907
9150
  const cellWidth = widths.slice(columnIndex, columnIndex + colspan).reduce((sum, width) => sum + width, 0);
9151
+ const cellRatio = columnRatios.slice(columnIndex, columnIndex + colspan).reduce((sum, ratio) => sum + ratio, 0);
8908
9152
  if (cellWidth > 0) {
8909
- cell.style.width = `${cellWidth}px`;
8910
- cell.style.minWidth = `${cellWidth}px`;
9153
+ cell.style.width = responsive ? formatBasisPointsAsPercentage(cellRatio) : `${cellWidth}px`;
9154
+ cell.style.minWidth = responsive ? "" : `${cellWidth}px`;
8911
9155
  }
8912
9156
  columnIndex += colspan;
8913
9157
  });
@@ -8925,6 +9169,7 @@ var init_preview_html = __esm({
8925
9169
  "src/components/UEditor/preview-html.ts"() {
8926
9170
  "use strict";
8927
9171
  init_table_dom_utils();
9172
+ init_table_width_model();
8928
9173
  DEFAULT_TABLE_COLUMN_WIDTH2 = 100;
8929
9174
  TIPTAP_TABLE_MIN_COLUMN_WIDTH = 25;
8930
9175
  }
@@ -36863,20 +37108,17 @@ var import_state6 = require("@tiptap/pm/state");
36863
37108
  var import_view2 = require("@tiptap/pm/view");
36864
37109
  var import_tables2 = require("@tiptap/pm/tables");
36865
37110
  init_table_dom_utils();
37111
+ init_table_width_model();
36866
37112
  var DEFAULT_TABLE_COLUMN_WIDTH = 100;
36867
37113
  var MIN_RESIZED_TABLE_COLUMN_WIDTH = 25;
36868
37114
  function getColumnResizeMinWidth(configuredMinWidth) {
36869
37115
  const normalizedMinWidth = Number.isFinite(configuredMinWidth) && configuredMinWidth > 0 ? Math.round(configuredMinWidth) : MIN_RESIZED_TABLE_COLUMN_WIDTH;
36870
37116
  return Math.max(MIN_RESIZED_TABLE_COLUMN_WIDTH, normalizedMinWidth);
36871
37117
  }
36872
- function setColumnStyle(column, width) {
36873
- if (width == null) {
36874
- column.style.width = `${DEFAULT_TABLE_COLUMN_WIDTH}px`;
36875
- column.style.minWidth = `${DEFAULT_TABLE_COLUMN_WIDTH}px`;
36876
- return;
36877
- }
36878
- column.style.width = `${Math.max(width, MIN_RESIZED_TABLE_COLUMN_WIDTH)}px`;
36879
- column.style.minWidth = "";
37118
+ function setColumnStyle(column, width, ratio, explicit, responsive) {
37119
+ column.style.width = responsive ? formatBasisPointsAsPercentage(ratio) : `${width}px`;
37120
+ column.style.minWidth = responsive || explicit ? "" : `${width}px`;
37121
+ column.setAttribute("width", String(width));
36880
37122
  }
36881
37123
  function isTableColumnElement(node) {
36882
37124
  return isCrossRealmElement(node) && String(node.tagName).toUpperCase() === "COL";
@@ -36885,6 +37127,7 @@ function updateDynamicColumns(node, colgroup, table, ownerDocument, overrideCol,
36885
37127
  let totalWidth = 0;
36886
37128
  let nextDOM = colgroup.firstChild;
36887
37129
  const row = node.firstChild;
37130
+ const columns = [];
36888
37131
  if (row) {
36889
37132
  for (let rowCellIndex = 0, col = 0; rowCellIndex < row.childCount; rowCellIndex += 1) {
36890
37133
  const { colspan, colwidth } = row.child(rowCellIndex).attrs;
@@ -36893,7 +37136,11 @@ function updateDynamicColumns(node, colgroup, table, ownerDocument, overrideCol,
36893
37136
  const width = rawWidth ? Math.max(rawWidth, MIN_RESIZED_TABLE_COLUMN_WIDTH) : null;
36894
37137
  totalWidth += width ?? DEFAULT_TABLE_COLUMN_WIDTH;
36895
37138
  const colElement = isTableColumnElement(nextDOM) ? nextDOM : colgroup.appendChild(ownerDocument.createElement("col"));
36896
- setColumnStyle(colElement, width);
37139
+ columns.push({
37140
+ element: colElement,
37141
+ explicit: width !== null,
37142
+ width: width ?? DEFAULT_TABLE_COLUMN_WIDTH
37143
+ });
36897
37144
  nextDOM = colElement.nextSibling;
36898
37145
  }
36899
37146
  }
@@ -36903,13 +37150,39 @@ function updateDynamicColumns(node, colgroup, table, ownerDocument, overrideCol,
36903
37150
  nextDOM.parentNode?.removeChild(nextDOM);
36904
37151
  nextDOM = after;
36905
37152
  }
36906
- const hasUserWidth = typeof node.attrs.style === "string" && /\bwidth\s*:/i.test(node.attrs.style);
36907
- if (!hasUserWidth) {
37153
+ const responsive = normalizeTableWidthMode(node.attrs.widthMode) === "responsive";
37154
+ const columnRatios = getTableColumnRatios(node);
37155
+ columns.forEach(({ element, explicit, width }, index) => {
37156
+ setColumnStyle(element, width, columnRatios[index] ?? 1, explicit, responsive);
37157
+ });
37158
+ if (responsive) {
37159
+ const widthBp = getResponsiveTableWidthBp(node);
37160
+ const offsetBp = getResponsiveTableOffsetBp(node);
37161
+ table.setAttribute("data-table-width-mode", "responsive");
37162
+ table.setAttribute("data-table-width", formatBasisPointsAsPercentage(widthBp));
37163
+ table.setAttribute("data-table-width-bp", String(widthBp));
37164
+ table.setAttribute("data-table-offset", formatBasisPointsAsPercentage(offsetBp));
37165
+ table.setAttribute("data-table-offset-bp", String(offsetBp));
37166
+ table.setAttribute("data-table-column-ratios", columnRatios.join(","));
37167
+ table.style.width = formatBasisPointsAsPercentage(widthBp);
37168
+ table.style.marginLeft = formatBasisPointsAsPercentage(offsetBp);
37169
+ table.style.marginRight = "auto";
37170
+ table.style.minWidth = `${Math.max(1, colgroup.childElementCount) * MIN_RESIZED_TABLE_COLUMN_WIDTH}px`;
37171
+ } else {
37172
+ table.removeAttribute("data-table-width-mode");
37173
+ table.removeAttribute("data-table-width");
37174
+ table.removeAttribute("data-table-width-bp");
37175
+ table.removeAttribute("data-table-offset");
37176
+ table.removeAttribute("data-table-offset-bp");
37177
+ table.removeAttribute("data-table-column-ratios");
36908
37178
  table.style.width = `${totalWidth}px`;
36909
37179
  table.style.minWidth = "";
36910
- } else {
36911
- table.style.minWidth = `${totalWidth}px`;
37180
+ const tableAlign = node.attrs.textAlign;
37181
+ table.style.marginLeft = tableAlign === "center" || tableAlign === "right" ? "auto" : "0px";
37182
+ table.style.marginRight = tableAlign === "center" ? "auto" : tableAlign === "right" ? "0px" : "auto";
36912
37183
  }
37184
+ if (node.attrs.textAlign) table.setAttribute("data-table-align", String(node.attrs.textAlign));
37185
+ else table.removeAttribute("data-table-align");
36913
37186
  }
36914
37187
  var UEditorTableView = class {
36915
37188
  constructor(node, _defaultColumnWidth, maybeView) {
@@ -36945,7 +37218,40 @@ var UEditorTableView = class {
36945
37218
  };
36946
37219
  function getDraggedWidth(dragging, event) {
36947
37220
  const offset = event.clientX - dragging.startX;
36948
- return Math.max(dragging.minWidth, Math.round(dragging.startWidth + offset));
37221
+ const maximum = dragging.neighborStartWidth === void 0 ? Number.POSITIVE_INFINITY : dragging.startWidth + dragging.neighborStartWidth - dragging.minWidth;
37222
+ return Math.min(maximum, Math.max(dragging.minWidth, Math.round(dragging.startWidth + offset)));
37223
+ }
37224
+ function normalizeColumnWidthsToTotal(values, total, minimum) {
37225
+ const safeTotal = Math.max(values.length * minimum, Math.round(total));
37226
+ const weights = values.map((value) => Number.isFinite(value) && value > 0 ? value : DEFAULT_TABLE_COLUMN_WIDTH);
37227
+ const weightSum = weights.reduce((sum, value) => sum + value, 0);
37228
+ const widths = weights.map((value) => Math.max(minimum, Math.round(value / weightSum * safeTotal)));
37229
+ let difference = safeTotal - widths.reduce((sum, value) => sum + value, 0);
37230
+ while (difference !== 0) {
37231
+ let changed = false;
37232
+ for (let index = widths.length - 1; index >= 0 && difference !== 0; index -= 1) {
37233
+ if (difference < 0 && widths[index] <= minimum) continue;
37234
+ widths[index] += difference > 0 ? 1 : -1;
37235
+ difference += difference > 0 ? -1 : 1;
37236
+ changed = true;
37237
+ }
37238
+ if (!changed) break;
37239
+ }
37240
+ return widths;
37241
+ }
37242
+ function getResizeColumnInfo(state, cell) {
37243
+ const $cell = state.doc.resolve(cell);
37244
+ const table = $cell.node(-1);
37245
+ const map = import_tables2.TableMap.get(table);
37246
+ const start = $cell.start(-1);
37247
+ const nodeAfter = $cell.nodeAfter;
37248
+ if (!nodeAfter) return null;
37249
+ return {
37250
+ col: map.colCount($cell.pos - start) + nodeAfter.attrs.colspan - 1,
37251
+ map,
37252
+ start,
37253
+ table
37254
+ };
36949
37255
  }
36950
37256
  function getCurrentColWidth(view, cellPos, { colspan, colwidth }) {
36951
37257
  const width = colwidth?.[colwidth.length - 1];
@@ -37014,6 +37320,12 @@ function handleMouseMove(view, event, handleWidth, lastColumnResizable) {
37014
37320
  if (event.clientX - left <= handleWidth) cell = edgeCell(view, event, "left", handleWidth);
37015
37321
  else if (right - event.clientX <= handleWidth) cell = edgeCell(view, event, "right", handleWidth);
37016
37322
  }
37323
+ if (cell !== -1) {
37324
+ const info = getResizeColumnInfo(view.state, cell);
37325
+ if (info && normalizeTableWidthMode(info.table.attrs.widthMode) === "responsive" && info.col === info.map.width - 1) {
37326
+ cell = -1;
37327
+ }
37328
+ }
37017
37329
  if (cell === pluginState.activeHandle) {
37018
37330
  clearHandleHoverTimer();
37019
37331
  return;
@@ -37060,33 +37372,47 @@ function handleMouseLeave(view) {
37060
37372
  updateHandle(view, -1);
37061
37373
  }
37062
37374
  }
37063
- function updateColumnWidth(view, cell, width) {
37064
- const $cell = view.state.doc.resolve(cell);
37065
- const table = $cell.node(-1);
37066
- const map = import_tables2.TableMap.get(table);
37067
- const start = $cell.start(-1);
37068
- const nodeAfter = $cell.nodeAfter;
37069
- if (!nodeAfter) return;
37070
- const col = map.colCount($cell.pos - start) + nodeAfter.attrs.colspan - 1;
37375
+ function updateColumnWidths(view, cell, widthsByColumn, columnRatios) {
37376
+ const info = getResizeColumnInfo(view.state, cell);
37377
+ if (!info) return;
37378
+ const { map, start, table } = info;
37071
37379
  const tr = view.state.tr;
37072
- for (let row = 0; row < map.height; row += 1) {
37073
- const mapIndex = row * map.width + col;
37074
- if (row && map.map[mapIndex] === map.map[mapIndex - map.width]) continue;
37075
- const pos = map.map[mapIndex];
37380
+ const seenCellPositions = /* @__PURE__ */ new Set();
37381
+ for (const pos of map.map) {
37382
+ if (seenCellPositions.has(pos)) continue;
37383
+ seenCellPositions.add(pos);
37076
37384
  const cellNode = table.nodeAt(pos);
37077
37385
  if (!cellNode) continue;
37078
37386
  const attrs = cellNode.attrs;
37079
- const index = attrs.colspan === 1 ? 0 : col - map.colCount(pos);
37080
- if (attrs.colwidth?.[index] === width) continue;
37081
37387
  const colwidth = attrs.colwidth ? attrs.colwidth.slice() : Array.from({ length: attrs.colspan }, () => 0);
37082
- colwidth[index] = width;
37388
+ const cellStartColumn = map.colCount(pos);
37389
+ let changed = false;
37390
+ for (let index = 0; index < attrs.colspan; index += 1) {
37391
+ const width = widthsByColumn.get(cellStartColumn + index);
37392
+ if (width === void 0 || colwidth[index] === width) continue;
37393
+ colwidth[index] = width;
37394
+ changed = true;
37395
+ }
37396
+ if (!changed) continue;
37083
37397
  tr.setNodeMarkup(start + pos, null, {
37084
37398
  ...attrs,
37085
37399
  colwidth
37086
37400
  });
37087
37401
  }
37402
+ if (columnRatios) {
37403
+ tr.setNodeMarkup(start - 1, void 0, {
37404
+ ...table.attrs,
37405
+ widthMode: "responsive",
37406
+ columnRatios: normalizeColumnRatios(columnRatios, map.width)
37407
+ });
37408
+ }
37088
37409
  if (tr.docChanged) view.dispatch(tr);
37089
37410
  }
37411
+ function updateColumnWidth(view, cell, width) {
37412
+ const info = getResizeColumnInfo(view.state, cell);
37413
+ if (!info) return;
37414
+ updateColumnWidths(view, cell, /* @__PURE__ */ new Map([[info.col, width]]));
37415
+ }
37090
37416
  function getActiveDragging(state) {
37091
37417
  const dragging = import_tables2.columnResizingPluginKey.getState(state)?.dragging;
37092
37418
  return dragging ? dragging : null;
@@ -37138,16 +37464,36 @@ function handleMouseDown(view, event, cellMinWidth) {
37138
37464
  if (!pluginState || pluginState.activeHandle === -1 || pluginState.dragging) return false;
37139
37465
  const cell = view.state.doc.nodeAt(pluginState.activeHandle);
37140
37466
  if (!cell) return false;
37467
+ const resizeInfo = getResizeColumnInfo(view.state, pluginState.activeHandle);
37468
+ if (!resizeInfo) return false;
37141
37469
  const attrs = cell.attrs;
37142
- const width = getCurrentColWidth(view, pluginState.activeHandle, {
37470
+ let width = getCurrentColWidth(view, pluginState.activeHandle, {
37143
37471
  colspan: attrs.colspan ?? 1,
37144
37472
  colwidth: attrs.colwidth
37145
37473
  });
37146
37474
  const minWidth = getColumnResizeMinWidth(cellMinWidth);
37475
+ let responsiveColumnWidths;
37476
+ let neighborStartWidth;
37477
+ if (normalizeTableWidthMode(resizeInfo.table.attrs.widthMode) === "responsive") {
37478
+ if (resizeInfo.col >= resizeInfo.map.width - 1) return false;
37479
+ const storedRatios = getTableColumnRatios(resizeInfo.table);
37480
+ const legacyWidths = getLegacyTableColumnWeights(resizeInfo.table, DEFAULT_TABLE_COLUMN_WIDTH);
37481
+ const storedTotal = legacyWidths.reduce((sum, value) => sum + value, 0);
37482
+ const tableElement = getTableElementAtCell(view, pluginState.activeHandle);
37483
+ const renderedWidth = tableElement?.getBoundingClientRect().width || storedTotal;
37484
+ responsiveColumnWidths = normalizeColumnWidthsToTotal(storedRatios, renderedWidth, minWidth);
37485
+ width = responsiveColumnWidths[resizeInfo.col];
37486
+ neighborStartWidth = responsiveColumnWidths[resizeInfo.col + 1];
37487
+ }
37147
37488
  const dragging = {
37148
37489
  startX: event.clientX,
37149
37490
  startWidth: width,
37150
- minWidth
37491
+ minWidth,
37492
+ ...responsiveColumnWidths ? {
37493
+ columnIndex: resizeInfo.col,
37494
+ responsiveColumnWidths,
37495
+ neighborStartWidth
37496
+ } : null
37151
37497
  };
37152
37498
  view.dispatch(view.state.tr.setMeta(import_tables2.columnResizingPluginKey, { setDragging: dragging }));
37153
37499
  function finish(nextEvent) {
@@ -37156,7 +37502,20 @@ function handleMouseDown(view, event, cellMinWidth) {
37156
37502
  const activeDragging = getActiveDragging(view.state);
37157
37503
  const activeHandle = import_tables2.columnResizingPluginKey.getState(view.state)?.activeHandle ?? -1;
37158
37504
  if (activeDragging && activeHandle > -1) {
37159
- updateColumnWidth(view, activeHandle, getDraggedWidth(activeDragging, nextEvent));
37505
+ const nextWidth = getDraggedWidth(activeDragging, nextEvent);
37506
+ if (activeDragging.responsiveColumnWidths && activeDragging.columnIndex !== void 0 && activeDragging.neighborStartWidth !== void 0) {
37507
+ const nextColumnWidths = activeDragging.responsiveColumnWidths.slice();
37508
+ nextColumnWidths[activeDragging.columnIndex] = nextWidth;
37509
+ nextColumnWidths[activeDragging.columnIndex + 1] = activeDragging.startWidth + activeDragging.neighborStartWidth - nextWidth;
37510
+ updateColumnWidths(
37511
+ view,
37512
+ activeHandle,
37513
+ new Map(nextColumnWidths.map((columnWidth, index) => [index, columnWidth])),
37514
+ normalizeColumnRatios(nextColumnWidths, nextColumnWidths.length)
37515
+ );
37516
+ } else {
37517
+ updateColumnWidth(view, activeHandle, nextWidth);
37518
+ }
37160
37519
  view.dispatch(view.state.tr.setMeta(import_tables2.columnResizingPluginKey, { setDragging: null }));
37161
37520
  }
37162
37521
  hideColumnResizeGhost(view);
@@ -37186,6 +37545,9 @@ function handleDecorations(state, cell, ownerDocument) {
37186
37545
  const nodeAfter = $cell.nodeAfter;
37187
37546
  if (!nodeAfter) return import_view2.DecorationSet.empty;
37188
37547
  const col = map.colCount($cell.pos - start) + nodeAfter.attrs.colspan - 1;
37548
+ if (normalizeTableWidthMode(table.attrs.widthMode) === "responsive" && col === map.width - 1) {
37549
+ return import_view2.DecorationSet.empty;
37550
+ }
37189
37551
  for (let row = 0; row < map.height; row += 1) {
37190
37552
  const index = col + row * map.width;
37191
37553
  if ((col === map.width - 1 || map.map[index] !== map.map[index + 1]) && (row === 0 || map.map[index] !== map.map[index - map.width])) {
@@ -37259,6 +37621,7 @@ function dynamicColumnResizing({
37259
37621
  // src/components/UEditor/table-align.ts
37260
37622
  init_table_align_utils();
37261
37623
  init_table_dom_utils();
37624
+ init_table_width_model();
37262
37625
  function normalizeTableAlign(value) {
37263
37626
  if (value === "left" || value === "center" || value === "right") {
37264
37627
  return value;
@@ -37275,7 +37638,7 @@ function parseTableAlign(element) {
37275
37638
  if ((marginLeft === "0px" || marginLeft === "0") && marginRight === "auto") return "left";
37276
37639
  return null;
37277
37640
  }
37278
- function renderTableAlignStyle(tableAlign) {
37641
+ function renderFixedTableAlignStyle(tableAlign) {
37279
37642
  switch (tableAlign) {
37280
37643
  case "center":
37281
37644
  return "table-layout: fixed; margin-left: auto; margin-right: auto;";
@@ -37287,28 +37650,61 @@ function renderTableAlignStyle(tableAlign) {
37287
37650
  return "";
37288
37651
  }
37289
37652
  }
37290
- function createUEditorColGroup(node) {
37291
- const columns = [];
37292
- let totalWidth = 0;
37293
- const firstRow = node.firstChild;
37294
- if (firstRow) {
37295
- for (let cellIndex = 0; cellIndex < firstRow.childCount; cellIndex += 1) {
37296
- const cell = firstRow.child(cellIndex);
37297
- const colspan = Math.max(1, Number(cell.attrs.colspan) || 1);
37298
- const colwidth = Array.isArray(cell.attrs.colwidth) ? cell.attrs.colwidth : [];
37299
- for (let spanIndex = 0; spanIndex < colspan; spanIndex += 1) {
37300
- const storedWidth = Number(colwidth[spanIndex]);
37301
- const width = Number.isFinite(storedWidth) && storedWidth > 0 ? Math.max(MIN_RESIZED_TABLE_COLUMN_WIDTH, Math.round(storedWidth)) : DEFAULT_TABLE_COLUMN_WIDTH;
37302
- totalWidth += width;
37303
- columns.push(["col", { style: `width: ${width}px; min-width: ${width}px;`, width: String(width) }]);
37304
- }
37305
- }
37653
+ function parseTableWidthMode(element) {
37654
+ const storedMode = element.getAttribute("data-table-width-mode");
37655
+ if (storedMode === "responsive" || storedMode === "full" || parsePercentageToBasisPoints(element.style.width) !== null) {
37656
+ return "responsive";
37306
37657
  }
37658
+ return "fixed";
37659
+ }
37660
+ function createUEditorColGroup(node, widthMode) {
37661
+ const columnWidths = getLegacyTableColumnWeights(node, DEFAULT_TABLE_COLUMN_WIDTH).map((width) => Math.max(MIN_RESIZED_TABLE_COLUMN_WIDTH, Math.round(width)));
37662
+ const columnRatios = getTableColumnRatios(node);
37663
+ const totalWidth = columnWidths.reduce((sum, width) => sum + width, 0);
37664
+ const columns = columnWidths.map((width, index) => ["col", {
37665
+ style: widthMode === "responsive" ? `width: ${formatBasisPointsAsPercentage(columnRatios[index])};` : `width: ${width}px; min-width: ${width}px;`,
37666
+ width: String(width)
37667
+ }]);
37307
37668
  return {
37308
37669
  colgroup: ["colgroup", {}, ...columns],
37670
+ columnCount: columns.length,
37309
37671
  tableWidth: totalWidth > 0 ? `${totalWidth}px` : ""
37310
37672
  };
37311
37673
  }
37674
+ function runResponsiveColumnStructureCommand(state, dispatch, command, updateLayout) {
37675
+ const tableInfo = findTableNodeInfoFromState(state);
37676
+ if (!tableInfo || normalizeTableWidthMode(tableInfo.node.attrs.widthMode) !== "responsive") {
37677
+ return command(state, dispatch);
37678
+ }
37679
+ const rect = (0, import_tables3.selectedRect)(state);
37680
+ const nextLayout = updateLayout({
37681
+ widthBp: getResponsiveTableWidthBp(tableInfo.node),
37682
+ columnRatios: getTableColumnRatios(tableInfo.node)
37683
+ }, rect);
37684
+ return command(state, dispatch ? (transaction) => {
37685
+ const nextTable = transaction.doc.nodeAt(tableInfo.pos);
37686
+ if (nextTable?.type.name === "table" && nextLayout.columnRatios.length > 0) {
37687
+ const widthBp = clampResponsiveTableWidthBp(nextLayout.widthBp);
37688
+ const tableAlign = normalizeTableAlign(nextTable.attrs.textAlign);
37689
+ const offsetBp = resolveResponsiveTableOffsetBp(
37690
+ widthBp,
37691
+ tableAlign,
37692
+ getResponsiveTableOffsetBp(tableInfo.node)
37693
+ );
37694
+ transaction.setNodeMarkup(tableInfo.pos, void 0, {
37695
+ ...nextTable.attrs,
37696
+ widthMode: "responsive",
37697
+ widthBp,
37698
+ offsetBp,
37699
+ columnRatios: normalizeColumnRatios(
37700
+ nextLayout.columnRatios,
37701
+ getLogicalTableColumnCount(nextTable)
37702
+ )
37703
+ });
37704
+ }
37705
+ dispatch(transaction);
37706
+ } : void 0);
37707
+ }
37312
37708
  var UEditorTable = import_extension_table.Table.extend({
37313
37709
  addGlobalAttributes() {
37314
37710
  return [
@@ -37326,10 +37722,64 @@ var UEditorTable = import_extension_table.Table.extend({
37326
37722
  const tableAlign = normalizeTableAlign(attributes.textAlign);
37327
37723
  if (!tableAlign) return {};
37328
37724
  return {
37329
- "data-table-align": tableAlign,
37330
- style: renderTableAlignStyle(tableAlign)
37725
+ "data-table-align": tableAlign
37331
37726
  };
37332
37727
  }
37728
+ },
37729
+ widthMode: {
37730
+ default: "fixed",
37731
+ parseHTML: (element) => {
37732
+ if (!(element instanceof HTMLElement)) return "fixed";
37733
+ return parseTableWidthMode(element);
37734
+ },
37735
+ renderHTML: (attributes) => {
37736
+ if (normalizeTableWidthMode(attributes.widthMode) !== "responsive") return {};
37737
+ return {
37738
+ "data-table-width-mode": "responsive"
37739
+ };
37740
+ }
37741
+ },
37742
+ widthBp: {
37743
+ default: null,
37744
+ parseHTML: (element) => {
37745
+ if (!(element instanceof HTMLElement) || parseTableWidthMode(element) !== "responsive") return null;
37746
+ const stored = Number(element.getAttribute("data-table-width-bp"));
37747
+ if (Number.isFinite(stored) && stored > 0) return clampResponsiveTableWidthBp(stored);
37748
+ return parsePercentageToBasisPoints(element.getAttribute("data-table-width") ?? element.style.width) ?? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP;
37749
+ },
37750
+ renderHTML: () => ({})
37751
+ },
37752
+ offsetBp: {
37753
+ default: null,
37754
+ parseHTML: (element) => {
37755
+ if (!(element instanceof HTMLElement) || parseTableWidthMode(element) !== "responsive") return null;
37756
+ const storedValue = element.getAttribute("data-table-offset-bp");
37757
+ const stored = Number(storedValue);
37758
+ if (storedValue !== null && Number.isFinite(stored) && stored >= 0) return Math.round(stored);
37759
+ const explicitOffset = parsePercentageToBasisPoints(
37760
+ element.getAttribute("data-table-offset") ?? element.style.marginLeft
37761
+ );
37762
+ if (explicitOffset !== null) return explicitOffset;
37763
+ const widthBp = parsePercentageToBasisPoints(
37764
+ element.getAttribute("data-table-width") ?? element.style.width
37765
+ ) ?? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP;
37766
+ const tableAlign = parseTableAlign(element);
37767
+ return resolveResponsiveTableOffsetBp(widthBp, tableAlign);
37768
+ },
37769
+ renderHTML: () => ({})
37770
+ },
37771
+ columnRatios: {
37772
+ default: null,
37773
+ parseHTML: (element) => {
37774
+ if (!(element instanceof HTMLElement)) return null;
37775
+ const storedRatios = parseColumnRatios(
37776
+ element.getAttribute("data-table-column-ratios") ?? element.getAttribute("data-column-ratios")
37777
+ );
37778
+ if (storedRatios) return storedRatios;
37779
+ const columnPercentages = Array.from(element.querySelectorAll("colgroup > col")).map((column) => parsePercentageToBasisPoints(column.style.width));
37780
+ return columnPercentages.length > 0 && columnPercentages.every((ratio) => ratio !== null) ? normalizeColumnRatios(columnPercentages, columnPercentages.length) : null;
37781
+ },
37782
+ renderHTML: () => ({})
37333
37783
  }
37334
37784
  }
37335
37785
  }
@@ -37338,13 +37788,86 @@ var UEditorTable = import_extension_table.Table.extend({
37338
37788
  addCommands() {
37339
37789
  return {
37340
37790
  ...this.parent?.(),
37791
+ insertTable: ({ rows = 3, cols = 3, withHeaderRow = true } = {}) => ({ tr, dispatch, editor }) => {
37792
+ const types = (0, import_tables3.tableNodeTypes)(editor.schema);
37793
+ const rowCount = Math.max(1, Math.floor(rows));
37794
+ const columnCount = Math.max(1, Math.floor(cols));
37795
+ const createCells = (type) => Array.from({ length: columnCount }, () => type.createAndFill()).filter((cell) => cell !== null);
37796
+ const bodyCells = createCells(types.cell);
37797
+ const headerCells = withHeaderRow ? createCells(types.header_cell) : bodyCells;
37798
+ const tableRows = Array.from({ length: rowCount }, (_, index) => types.row.createChecked(
37799
+ null,
37800
+ withHeaderRow && index === 0 ? headerCells : bodyCells
37801
+ ));
37802
+ const table = types.table.createChecked(
37803
+ {
37804
+ widthMode: "responsive",
37805
+ widthBp: DEFAULT_RESPONSIVE_TABLE_WIDTH_BP,
37806
+ offsetBp: 0,
37807
+ columnRatios: normalizeColumnRatios(null, columnCount)
37808
+ },
37809
+ tableRows
37810
+ );
37811
+ if (dispatch) {
37812
+ const offset = tr.selection.from + 1;
37813
+ tr.replaceSelectionWith(table).scrollIntoView().setSelection(import_state7.TextSelection.near(tr.doc.resolve(offset)));
37814
+ }
37815
+ return true;
37816
+ },
37817
+ addColumnBefore: () => ({ state, dispatch }) => {
37818
+ return runResponsiveColumnStructureCommand(
37819
+ state,
37820
+ dispatch,
37821
+ import_tables3.addColumnBefore,
37822
+ (layout, rect) => insertResponsiveColumnLayout(
37823
+ layout.widthBp,
37824
+ layout.columnRatios,
37825
+ rect.left,
37826
+ rect.left
37827
+ )
37828
+ );
37829
+ },
37830
+ addColumnAfter: () => ({ state, dispatch }) => {
37831
+ return runResponsiveColumnStructureCommand(
37832
+ state,
37833
+ dispatch,
37834
+ import_tables3.addColumnAfter,
37835
+ (layout, rect) => insertResponsiveColumnLayout(
37836
+ layout.widthBp,
37837
+ layout.columnRatios,
37838
+ rect.right,
37839
+ rect.right - 1
37840
+ )
37841
+ );
37842
+ },
37843
+ deleteColumn: () => ({ state, dispatch }) => {
37844
+ return runResponsiveColumnStructureCommand(
37845
+ state,
37846
+ dispatch,
37847
+ import_tables3.deleteColumn,
37848
+ (layout, rect) => {
37849
+ let nextLayout = layout;
37850
+ for (let index = rect.right - 1; index >= rect.left; index -= 1) {
37851
+ nextLayout = deleteResponsiveColumnLayout(
37852
+ nextLayout.widthBp,
37853
+ nextLayout.columnRatios,
37854
+ index
37855
+ );
37856
+ }
37857
+ return nextLayout;
37858
+ }
37859
+ );
37860
+ },
37341
37861
  setTableAlign: (tableAlign) => ({ state, dispatch }) => {
37342
37862
  const tableInfo = findTableNodeInfoFromState(state);
37343
37863
  if (!tableInfo) return false;
37864
+ const widthBp = getResponsiveTableWidthBp(tableInfo.node);
37865
+ const offsetBp = resolveResponsiveTableOffsetBp(widthBp, tableAlign);
37344
37866
  dispatch?.(
37345
37867
  state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
37346
37868
  ...tableInfo.node.attrs,
37347
- textAlign: tableAlign
37869
+ textAlign: tableAlign,
37870
+ ...normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive" ? { offsetBp } : null
37348
37871
  })
37349
37872
  );
37350
37873
  return true;
@@ -37363,11 +37886,31 @@ var UEditorTable = import_extension_table.Table.extend({
37363
37886
  };
37364
37887
  },
37365
37888
  renderHTML({ node, HTMLAttributes }) {
37366
- const { colgroup, tableWidth } = createUEditorColGroup(node);
37889
+ const widthMode = normalizeTableWidthMode(node.attrs.widthMode);
37890
+ const { colgroup, columnCount, tableWidth } = createUEditorColGroup(node, widthMode);
37891
+ const tableAlign = normalizeTableAlign(node.attrs.textAlign);
37892
+ const widthBp = getResponsiveTableWidthBp(node);
37893
+ const offsetBp = getResponsiveTableOffsetBp(node);
37894
+ const columnRatios = getTableColumnRatios(node);
37895
+ const tableStyle = widthMode === "responsive" ? [
37896
+ `width: ${formatBasisPointsAsPercentage(widthBp)}`,
37897
+ `margin-left: ${formatBasisPointsAsPercentage(offsetBp)}`,
37898
+ "margin-right: auto",
37899
+ `min-width: ${Math.max(1, columnCount) * MIN_RESIZED_TABLE_COLUMN_WIDTH}px`,
37900
+ "table-layout: fixed"
37901
+ ].join("; ") + ";" : tableWidth ? `width: ${tableWidth}; ${renderFixedTableAlignStyle(tableAlign)}` : `table-layout: fixed; ${renderFixedTableAlignStyle(tableAlign)}`;
37367
37902
  const table = [
37368
37903
  "table",
37369
37904
  (0, import_core17.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, {
37370
- style: tableWidth ? `width: ${tableWidth};` : "table-layout: fixed;"
37905
+ ...widthMode === "responsive" ? {
37906
+ "data-table-width-mode": "responsive",
37907
+ "data-table-width": formatBasisPointsAsPercentage(widthBp),
37908
+ "data-table-width-bp": String(widthBp),
37909
+ "data-table-offset": formatBasisPointsAsPercentage(offsetBp),
37910
+ "data-table-offset-bp": String(offsetBp),
37911
+ "data-table-column-ratios": columnRatios.join(",")
37912
+ } : null,
37913
+ style: tableStyle
37371
37914
  }),
37372
37915
  colgroup,
37373
37916
  ["tbody", 0]
@@ -37420,6 +37963,31 @@ var UEditorTable = import_extension_table.Table.extend({
37420
37963
  (0, import_tables3.tableEditing)({
37421
37964
  allowTableNodeSelection: this.options.allowTableNodeSelection
37422
37965
  }),
37966
+ new import_state7.Plugin({
37967
+ key: new import_state7.PluginKey("responsiveTableLayoutNormalizer"),
37968
+ appendTransaction(transactions, _oldState, newState) {
37969
+ if (!transactions.some((transaction) => transaction.docChanged)) return null;
37970
+ const tableInfo = findTableNodeInfoFromState(newState);
37971
+ const node = tableInfo?.node;
37972
+ if (!tableInfo || !node || normalizeTableWidthMode(node.attrs.widthMode) !== "responsive") return null;
37973
+ const columnCount = getLogicalTableColumnCount(node);
37974
+ const columnRatios = getTableColumnRatios(node);
37975
+ const widthBp = getResponsiveTableWidthBp(node);
37976
+ const offsetBp = getResponsiveTableOffsetBp(node);
37977
+ const storedRatios = parseColumnRatios(node.attrs.columnRatios);
37978
+ const ratiosMatch = storedRatios?.length === columnCount && storedRatios.every((ratio, index) => Math.round(ratio) === columnRatios[index]);
37979
+ if (ratiosMatch && node.attrs.widthBp === widthBp && node.attrs.offsetBp === offsetBp && node.attrs.widthMode === "responsive") {
37980
+ return null;
37981
+ }
37982
+ return newState.tr.setNodeMarkup(tableInfo.pos, void 0, {
37983
+ ...node.attrs,
37984
+ widthMode: "responsive",
37985
+ widthBp,
37986
+ offsetBp,
37987
+ columnRatios
37988
+ });
37989
+ }
37990
+ }),
37423
37991
  new import_state7.Plugin({
37424
37992
  appendTransaction(_transactions, _oldState, newState) {
37425
37993
  const { doc, schema } = newState;
@@ -43927,7 +44495,7 @@ var Selection = class {
43927
44495
  found.
43928
44496
  */
43929
44497
  static findFrom($pos, dir, textOnly = false) {
43930
- let inner = $pos.parent.inlineContent ? new TextSelection5($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
44498
+ let inner = $pos.parent.inlineContent ? new TextSelection6($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
43931
44499
  if (inner)
43932
44500
  return inner;
43933
44501
  for (let depth = $pos.depth - 1; depth >= 0; depth--) {
@@ -43996,7 +44564,7 @@ var Selection = class {
43996
44564
  returns the bookmark for that.
43997
44565
  */
43998
44566
  getBookmark() {
43999
- return TextSelection5.between(this.$anchor, this.$head).getBookmark();
44567
+ return TextSelection6.between(this.$anchor, this.$head).getBookmark();
44000
44568
  }
44001
44569
  };
44002
44570
  Selection.prototype.visible = true;
@@ -44016,7 +44584,7 @@ function checkTextSelection($pos) {
44016
44584
  console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")");
44017
44585
  }
44018
44586
  }
44019
- var TextSelection5 = class _TextSelection extends Selection {
44587
+ var TextSelection6 = class _TextSelection extends Selection {
44020
44588
  /**
44021
44589
  Construct a text selection between the given points.
44022
44590
  */
@@ -44102,7 +44670,7 @@ var TextSelection5 = class _TextSelection extends Selection {
44102
44670
  return new _TextSelection($anchor, $head);
44103
44671
  }
44104
44672
  };
44105
- Selection.jsonID("text", TextSelection5);
44673
+ Selection.jsonID("text", TextSelection6);
44106
44674
  var TextBookmark = class _TextBookmark {
44107
44675
  constructor(anchor, head) {
44108
44676
  this.anchor = anchor;
@@ -44112,7 +44680,7 @@ var TextBookmark = class _TextBookmark {
44112
44680
  return new _TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
44113
44681
  }
44114
44682
  resolve(doc) {
44115
- return TextSelection5.between(doc.resolve(this.anchor), doc.resolve(this.head));
44683
+ return TextSelection6.between(doc.resolve(this.anchor), doc.resolve(this.head));
44116
44684
  }
44117
44685
  };
44118
44686
  var NodeSelection2 = class _NodeSelection extends Selection {
@@ -44231,7 +44799,7 @@ var AllBookmark = {
44231
44799
  };
44232
44800
  function findSelectionIn(doc, node, pos, index, dir, text = false) {
44233
44801
  if (node.inlineContent)
44234
- return TextSelection5.create(doc, pos);
44802
+ return TextSelection6.create(doc, pos);
44235
44803
  for (let i = index - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
44236
44804
  let child = node.child(i);
44237
44805
  if (!child.isAtom) {
@@ -44731,7 +45299,7 @@ function freshColWidth(attrs) {
44731
45299
  for (let i = 0; i < attrs.colspan; i++) result.push(0);
44732
45300
  return result;
44733
45301
  }
44734
- function tableNodeTypes2(schema) {
45302
+ function tableNodeTypes3(schema) {
44735
45303
  let result = schema.cached.tableNodeTypes;
44736
45304
  if (!result) {
44737
45305
  result = schema.cached.tableNodeTypes = {};
@@ -44823,7 +45391,7 @@ var CellSelection2 = class CellSelection3 extends Selection {
44823
45391
  else if (tableChanged && this.isColSelection()) return CellSelection3.colSelection($anchorCell, $headCell);
44824
45392
  else return new CellSelection3($anchorCell, $headCell);
44825
45393
  }
44826
- return TextSelection5.between($anchorCell, $headCell);
45394
+ return TextSelection6.between($anchorCell, $headCell);
44827
45395
  }
44828
45396
  content() {
44829
45397
  const table = this.$anchorCell.node(-1);
@@ -45248,7 +45816,7 @@ function moveTableRow$1(table, indexesOrigin, indexesTarget, direction) {
45248
45816
  rows = moveRowInArrayOfRows(rows, indexesOrigin, indexesTarget, direction);
45249
45817
  return convertArrayOfRowsToTableNode(table, rows);
45250
45818
  }
45251
- function selectedRect4(state) {
45819
+ function selectedRect5(state) {
45252
45820
  const sel = state.selection;
45253
45821
  const $pos = selectionCell(state);
45254
45822
  const table = $pos.node(-1);
@@ -45265,8 +45833,8 @@ function deprecated_toggleHeader(type) {
45265
45833
  return function(state, dispatch) {
45266
45834
  if (!isInTable2(state)) return false;
45267
45835
  if (dispatch) {
45268
- const types = tableNodeTypes2(state.schema);
45269
- const rect = selectedRect4(state), tr = state.tr;
45836
+ const types = tableNodeTypes3(state.schema);
45837
+ const rect = selectedRect5(state), tr = state.tr;
45270
45838
  const cells = rect.map.cellsInRect(type == "column" ? {
45271
45839
  left: rect.left,
45272
45840
  top: 0,
@@ -45305,8 +45873,8 @@ function toggleHeader(type, options) {
45305
45873
  return function(state, dispatch) {
45306
45874
  if (!isInTable2(state)) return false;
45307
45875
  if (dispatch) {
45308
- const types = tableNodeTypes2(state.schema);
45309
- const rect = selectedRect4(state), tr = state.tr;
45876
+ const types = tableNodeTypes3(state.schema);
45877
+ const rect = selectedRect5(state), tr = state.tr;
45310
45878
  const isHeaderRowEnabled = isHeaderEnabledByType("row", rect, types);
45311
45879
  const isHeaderColumnEnabled = isHeaderEnabledByType("column", rect, types);
45312
45880
  const selectionStartsAt = (type === "column" ? isHeaderRowEnabled : type === "row" ? isHeaderColumnEnabled : false) ? 1 : 0;
@@ -45340,7 +45908,7 @@ function deleteCellSelection(state, dispatch) {
45340
45908
  if (!(sel instanceof CellSelection2)) return false;
45341
45909
  if (dispatch) {
45342
45910
  const tr = state.tr;
45343
- const baseContent = tableNodeTypes2(state.schema).cell.createAndFill().content;
45911
+ const baseContent = tableNodeTypes3(state.schema).cell.createAndFill().content;
45344
45912
  sel.forEachCell((cell, pos) => {
45345
45913
  if (!cell.content.eq(baseContent)) tr.replace(tr.mapping.map(pos + 1), tr.mapping.map(pos + cell.nodeSize - 1), new Slice(baseContent, 0, 0));
45346
45914
  });
@@ -45438,7 +46006,7 @@ function shiftArrow(axis, dir) {
45438
46006
  };
45439
46007
  }
45440
46008
  function atEndOfCell(view, axis, dir) {
45441
- if (!(view.state.selection instanceof TextSelection5)) return null;
46009
+ if (!(view.state.selection instanceof TextSelection6)) return null;
45442
46010
  const { $head } = view.state.selection;
45443
46011
  for (let d = $head.depth - 1; d >= 0; d--) {
45444
46012
  const parent = $head.node(d);
@@ -45465,6 +46033,7 @@ init_table_dom_utils();
45465
46033
 
45466
46034
  // src/components/UEditor/table-layout-model.ts
45467
46035
  init_table_dom_utils();
46036
+ init_table_width_model();
45468
46037
  var FALLBACK_TABLE_ROW_HEIGHT = DEFAULT_TABLE_ROW_HEIGHT;
45469
46038
  var FALLBACK_TABLE_COLUMN_WIDTH = 160;
45470
46039
  function isTableCellElement(element) {
@@ -45751,7 +46320,8 @@ function buildTableControlLayout(editor, surface, cell) {
45751
46320
  avgRowHeight,
45752
46321
  avgColumnWidth,
45753
46322
  rowHandles,
45754
- columnHandles
46323
+ columnHandles,
46324
+ widthMode: normalizeTableWidthMode(tableInfo.node.attrs.widthMode)
45755
46325
  };
45756
46326
  }
45757
46327
 
@@ -46249,11 +46819,17 @@ var HANDLE_BASE_CLASS = cn(
46249
46819
  "hover:bg-primary hover:text-primary-foreground hover:shadow-md active:scale-95",
46250
46820
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
46251
46821
  );
46822
+ var EDGE_HANDLE_CLASS = cn(
46823
+ "pointer-events-auto absolute top-0 z-10 h-full w-2 touch-none cursor-col-resize bg-transparent p-0",
46824
+ "border-transparent transition-colors duration-150 hover:border-primary",
46825
+ "focus-visible:border-primary focus-visible:outline-none"
46826
+ );
46252
46827
  function TableResizeHandles({
46253
46828
  active,
46254
46829
  ctrlHint,
46255
46830
  frameRef,
46256
46831
  layout,
46832
+ onFitWidth,
46257
46833
  onStartResize,
46258
46834
  resizeBothLabel
46259
46835
  }) {
@@ -46274,6 +46850,36 @@ function TableResizeHandles({
46274
46850
  height: layout.tableHeight
46275
46851
  },
46276
46852
  children: [
46853
+ layout.widthMode === "responsive" ? /* @__PURE__ */ (0, import_jsx_runtime101.jsxs)(import_jsx_runtime101.Fragment, { children: [
46854
+ /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
46855
+ "button",
46856
+ {
46857
+ type: "button",
46858
+ "aria-label": `${resizeBothLabel} \u2014 \u2190`,
46859
+ title: resizeBothLabel,
46860
+ "data-table-resize-handle": "left",
46861
+ className: cn(
46862
+ EDGE_HANDLE_CLASS,
46863
+ "left-0 border-l-2"
46864
+ ),
46865
+ onPointerDown: (event) => onStartResize(event, "left")
46866
+ }
46867
+ ),
46868
+ /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
46869
+ "button",
46870
+ {
46871
+ type: "button",
46872
+ "aria-label": `${resizeBothLabel} \u2014 \u2192`,
46873
+ title: resizeBothLabel,
46874
+ "data-table-resize-handle": "right",
46875
+ className: cn(
46876
+ EDGE_HANDLE_CLASS,
46877
+ "right-0 border-r-2"
46878
+ ),
46879
+ onPointerDown: (event) => onStartResize(event, "right")
46880
+ }
46881
+ )
46882
+ ] }) : null,
46277
46883
  /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
46278
46884
  "button",
46279
46885
  {
@@ -46286,7 +46892,8 @@ function TableResizeHandles({
46286
46892
  "bottom-[-28px] right-[-30px] h-6 w-6 cursor-nwse-resize",
46287
46893
  active && "bg-primary text-primary-foreground shadow-md"
46288
46894
  ),
46289
- onPointerDown: onStartResize,
46895
+ onDoubleClick: onFitWidth,
46896
+ onPointerDown: (event) => onStartResize(event, "both"),
46290
46897
  children: /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(import_lucide_react56.MoveDiagonal2, { "aria-hidden": "true", className: "h-3.5 w-3.5", strokeWidth: 2.25 })
46291
46898
  }
46292
46899
  ),
@@ -46307,6 +46914,7 @@ function TableResizeHandles({
46307
46914
 
46308
46915
  // src/components/UEditor/table-size-utils.ts
46309
46916
  init_table_dom_utils();
46917
+ init_table_width_model();
46310
46918
  var MAX_TABLE_DIMENSION = 8192;
46311
46919
  function positiveMetric(value, fallback) {
46312
46920
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
@@ -46349,7 +46957,7 @@ function getLogicalRowHeights(table, fallback) {
46349
46957
  });
46350
46958
  return heights;
46351
46959
  }
46352
- function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight) {
46960
+ function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight, containerWidth = startWidth) {
46353
46961
  const tableInfo = findTableInfo(editor, anchorPos);
46354
46962
  if (!tableInfo) return null;
46355
46963
  const tableMap = TableMap4.get(tableInfo.node);
@@ -46358,13 +46966,16 @@ function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight) {
46358
46966
  const safeHeight = Math.max(tableMap.height * MIN_TABLE_ROW_HEIGHT, Math.round(startHeight));
46359
46967
  const fallbackColumnWidth = safeWidth / tableMap.width;
46360
46968
  const fallbackRowHeight = safeHeight / tableMap.height;
46969
+ const widthMode = normalizeTableWidthMode(tableInfo.node.attrs.widthMode);
46970
+ const safeContainerWidth = Math.max(1, Math.round(containerWidth));
46971
+ const columnWeights = widthMode === "responsive" ? getTableColumnRatios(tableInfo.node) : getLogicalColumnWidths(tableInfo.node, tableMap, fallbackColumnWidth);
46361
46972
  return {
46362
46973
  anchorPos,
46363
46974
  tablePos: tableInfo.pos,
46364
46975
  startWidth: safeWidth,
46365
46976
  startHeight: safeHeight,
46366
46977
  columnWidths: normalizeWeightsToTotal(
46367
- getLogicalColumnWidths(tableInfo.node, tableMap, fallbackColumnWidth),
46978
+ columnWeights,
46368
46979
  safeWidth,
46369
46980
  MIN_RESIZED_TABLE_COLUMN_WIDTH
46370
46981
  ),
@@ -46374,7 +46985,12 @@ function createTableSizeSnapshot(editor, anchorPos, startWidth, startHeight) {
46374
46985
  MIN_TABLE_ROW_HEIGHT
46375
46986
  ),
46376
46987
  minWidth: tableMap.width * MIN_RESIZED_TABLE_COLUMN_WIDTH,
46377
- minHeight: tableMap.height * MIN_TABLE_ROW_HEIGHT
46988
+ minHeight: tableMap.height * MIN_TABLE_ROW_HEIGHT,
46989
+ containerWidth: safeContainerWidth,
46990
+ widthMode,
46991
+ widthBp: getResponsiveTableWidthBp(tableInfo.node),
46992
+ offsetBp: getResponsiveTableOffsetBp(tableInfo.node),
46993
+ columnRatios: getTableColumnRatios(tableInfo.node)
46378
46994
  };
46379
46995
  }
46380
46996
  function resolveTableResizeDimensions({
@@ -46382,12 +46998,14 @@ function resolveTableResizeDimensions({
46382
46998
  deltaY,
46383
46999
  lockAxis,
46384
47000
  preserveRatio,
47001
+ edge = "both",
46385
47002
  snapshot
46386
47003
  }) {
46387
- let width = snapshot.startWidth + deltaX;
46388
- let height = snapshot.startHeight + deltaY;
47004
+ const horizontalDelta = edge === "left" ? -deltaX : deltaX;
47005
+ let width = snapshot.startWidth + horizontalDelta;
47006
+ let height = edge === "both" ? snapshot.startHeight + deltaY : snapshot.startHeight;
46389
47007
  if (preserveRatio) {
46390
- const horizontalDrag = Math.abs(deltaX) >= Math.abs(deltaY);
47008
+ const horizontalDrag = edge !== "both" || Math.abs(deltaX) >= Math.abs(deltaY);
46391
47009
  const scale = horizontalDrag ? width / snapshot.startWidth : height / snapshot.startHeight;
46392
47010
  const minScale = Math.max(
46393
47011
  snapshot.minWidth / snapshot.startWidth,
@@ -46400,22 +47018,56 @@ function resolveTableResizeDimensions({
46400
47018
  const safeScale = Math.min(Math.max(scale, minScale), maxScale);
46401
47019
  width = snapshot.startWidth * safeScale;
46402
47020
  height = snapshot.startHeight * safeScale;
46403
- } else if (lockAxis) {
47021
+ } else if (lockAxis && edge === "both") {
46404
47022
  if (Math.abs(deltaX) >= Math.abs(deltaY)) {
46405
47023
  height = snapshot.startHeight;
46406
47024
  } else {
46407
47025
  width = snapshot.startWidth;
46408
47026
  }
46409
47027
  }
47028
+ let nextWidth = Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minWidth, Math.round(width)));
47029
+ const nextHeight = Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minHeight, Math.round(height)));
47030
+ if (snapshot.widthMode !== "responsive") {
47031
+ return { width: nextWidth, height: nextHeight };
47032
+ }
47033
+ const containerWidth = Math.max(1, snapshot.containerWidth);
47034
+ const startOffset = snapshot.offsetBp / TABLE_WIDTH_BASIS_POINTS * containerWidth;
47035
+ let nextOffset = startOffset;
47036
+ if (edge === "left") {
47037
+ const fixedRightEdge = startOffset + snapshot.startWidth;
47038
+ nextOffset = Math.min(
47039
+ fixedRightEdge - snapshot.minWidth,
47040
+ Math.max(0, startOffset + deltaX)
47041
+ );
47042
+ nextWidth = fixedRightEdge - nextOffset;
47043
+ }
47044
+ const widthBp = clampResponsiveTableWidthBp(
47045
+ nextWidth / containerWidth * TABLE_WIDTH_BASIS_POINTS
47046
+ );
47047
+ const offsetBp = resolveResponsiveTableOffsetBp(
47048
+ widthBp,
47049
+ null,
47050
+ nextOffset / containerWidth * TABLE_WIDTH_BASIS_POINTS
47051
+ );
46410
47052
  return {
46411
- width: Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minWidth, Math.round(width))),
46412
- height: Math.min(MAX_TABLE_DIMENSION, Math.max(snapshot.minHeight, Math.round(height)))
47053
+ width: Math.round(widthBp / TABLE_WIDTH_BASIS_POINTS * containerWidth),
47054
+ height: nextHeight,
47055
+ widthBp,
47056
+ offsetBp,
47057
+ leftDelta: nextOffset - startOffset
46413
47058
  };
46414
47059
  }
46415
47060
  function arraysEqual(left, right) {
46416
47061
  return Array.isArray(left) && left.length === right.length && left.every((value, index) => value === right[index]);
46417
47062
  }
46418
- function applyTableSize(editor, snapshot, dimensions) {
47063
+ function getResponsiveResizeAlignment(widthBp, offsetBp) {
47064
+ const gap = Math.max(0, TABLE_WIDTH_BASIS_POINTS - widthBp);
47065
+ if (offsetBp === 0) return "left";
47066
+ if (offsetBp === gap) return "right";
47067
+ if (Math.abs(offsetBp * 2 - gap) <= 1) return "center";
47068
+ return null;
47069
+ }
47070
+ function applyTableSize(editor, snapshot, dimensions, options = {}) {
46419
47071
  const table = editor.state.doc.nodeAt(snapshot.tablePos);
46420
47072
  if (!table || table.type.name !== "table") return false;
46421
47073
  const tableMap = TableMap4.get(table);
@@ -46424,9 +47076,32 @@ function applyTableSize(editor, snapshot, dimensions) {
46424
47076
  }
46425
47077
  const resizeWidth = dimensions.width !== snapshot.startWidth;
46426
47078
  const resizeHeight = dimensions.height !== snapshot.startHeight;
46427
- if (!resizeWidth && !resizeHeight) return false;
47079
+ const widthMode = options.widthMode ?? snapshot.widthMode;
47080
+ const widthModeChanged = normalizeTableWidthMode(table.attrs.widthMode) !== widthMode || widthMode === "responsive" && table.attrs.widthMode !== "responsive";
47081
+ const widthBp = dimensions.widthBp ?? clampResponsiveTableWidthBp(
47082
+ dimensions.width / Math.max(1, snapshot.containerWidth) * TABLE_WIDTH_BASIS_POINTS
47083
+ );
47084
+ const offsetBp = resolveResponsiveTableOffsetBp(
47085
+ widthBp,
47086
+ null,
47087
+ dimensions.offsetBp ?? snapshot.offsetBp
47088
+ );
47089
+ const responsiveLayoutChanged = widthMode === "responsive" && (table.attrs.widthBp !== widthBp || table.attrs.offsetBp !== offsetBp);
47090
+ if (!resizeWidth && !resizeHeight && !widthModeChanged && !responsiveLayoutChanged) return false;
46428
47091
  const tableStart = snapshot.tablePos + 1;
46429
47092
  const transaction = editor.state.tr;
47093
+ if (widthModeChanged || responsiveLayoutChanged) {
47094
+ transaction.setNodeMarkup(snapshot.tablePos, void 0, {
47095
+ ...table.attrs,
47096
+ widthMode,
47097
+ ...widthMode === "responsive" ? {
47098
+ widthBp,
47099
+ offsetBp,
47100
+ columnRatios: normalizeColumnRatios(snapshot.columnRatios, tableMap.width),
47101
+ textAlign: getResponsiveResizeAlignment(widthBp, offsetBp)
47102
+ } : null
47103
+ });
47104
+ }
46430
47105
  if (resizeWidth) {
46431
47106
  const nextColumnWidths = normalizeWeightsToTotal(
46432
47107
  snapshot.columnWidths,
@@ -46469,6 +47144,7 @@ function applyTableSize(editor, snapshot, dimensions) {
46469
47144
  }
46470
47145
 
46471
47146
  // src/components/UEditor/table-controls.tsx
47147
+ init_table_width_model();
46472
47148
  var import_jsx_runtime102 = require("react/jsx-runtime");
46473
47149
  var TABLE_MENU_TOP_OFFSET = 10;
46474
47150
  var AXIS_HANDLE_RADIUS = 12;
@@ -46578,6 +47254,10 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
46578
47254
  if (!frame) return;
46579
47255
  frame.style.width = `${dimensions.width}px`;
46580
47256
  frame.style.height = `${dimensions.height}px`;
47257
+ const activeLayout = layoutRef.current;
47258
+ if (activeLayout) {
47259
+ frame.style.left = `${activeLayout.tableLeft + (dimensions.leftDelta ?? 0)}px`;
47260
+ }
46581
47261
  const dimensionsLabel = frame.querySelector("[data-table-resize-dimensions]");
46582
47262
  if (dimensionsLabel) {
46583
47263
  dimensionsLabel.textContent = `${dimensions.width} \xD7 ${dimensions.height}`;
@@ -46588,7 +47268,8 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
46588
47268
  if (!activeLayout) return;
46589
47269
  updateTableResizePreview({
46590
47270
  width: Math.round(activeLayout.tableWidth),
46591
- height: Math.round(activeLayout.tableHeight)
47271
+ height: Math.round(activeLayout.tableHeight),
47272
+ leftDelta: 0
46592
47273
  });
46593
47274
  }, [updateTableResizePreview]);
46594
47275
  const clearDrag = import_react77.default.useCallback(() => {
@@ -46753,7 +47434,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
46753
47434
  const canExpandTable = Boolean(layout);
46754
47435
  const controlsVisible = false;
46755
47436
  const tableMenuOpen = openMenuKey === "table";
46756
- const startTableResize = import_react77.default.useCallback((event) => {
47437
+ const startTableResize = import_react77.default.useCallback((event, edge) => {
46757
47438
  if (event.button !== 0 || dragStateRef.current) return;
46758
47439
  const activeLayout = layoutRef.current;
46759
47440
  if (!activeLayout) return;
@@ -46761,7 +47442,8 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
46761
47442
  editor,
46762
47443
  activeLayout.cellPos,
46763
47444
  activeLayout.tableWidth,
46764
- activeLayout.tableHeight
47445
+ activeLayout.tableHeight,
47446
+ activeLayout.viewportWidth
46765
47447
  );
46766
47448
  if (!snapshot) return;
46767
47449
  event.preventDefault();
@@ -46783,12 +47465,38 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
46783
47465
  pointerTarget: event.currentTarget,
46784
47466
  startX: event.clientX,
46785
47467
  startY: event.clientY,
47468
+ edge,
46786
47469
  snapshot,
46787
47470
  pendingDimensions
46788
47471
  };
46789
47472
  updateTableResizePreview(pendingDimensions);
46790
- setDocumentCursor(editorDocument, "nwse-resize");
47473
+ setDocumentCursor(editorDocument, edge === "both" ? "nwse-resize" : "ew-resize");
46791
47474
  }, [editor, editorDocument, updateTableResizePreview]);
47475
+ const fitTableToEditorWidth = import_react77.default.useCallback((event) => {
47476
+ if (event.button !== 0 || dragStateRef.current) return;
47477
+ const activeLayout = layoutRef.current;
47478
+ if (!activeLayout) return;
47479
+ const snapshot = createTableSizeSnapshot(
47480
+ editor,
47481
+ activeLayout.cellPos,
47482
+ activeLayout.tableWidth,
47483
+ activeLayout.tableHeight,
47484
+ activeLayout.viewportWidth
47485
+ );
47486
+ if (!snapshot) return;
47487
+ event.preventDefault();
47488
+ event.stopPropagation();
47489
+ const didResize = applyTableSize(editor, snapshot, {
47490
+ width: Math.round(activeLayout.viewportWidth),
47491
+ height: snapshot.startHeight,
47492
+ widthBp: TABLE_WIDTH_BASIS_POINTS,
47493
+ offsetBp: 0,
47494
+ leftDelta: -(snapshot.offsetBp / TABLE_WIDTH_BASIS_POINTS * snapshot.containerWidth)
47495
+ }, { widthMode: "responsive" });
47496
+ if (didResize) {
47497
+ scheduleSyncFromSelection();
47498
+ }
47499
+ }, [editor, scheduleSyncFromSelection]);
46792
47500
  const startAddColumnDrag = import_react77.default.useCallback(() => {
46793
47501
  setOpenMenuKey(null);
46794
47502
  dragStateRef.current = { kind: "add-column", previewCols: 1 };
@@ -46903,12 +47611,30 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
46903
47611
  scheduleSyncFromSelection();
46904
47612
  }
46905
47613
  if (dragState.kind === "column" && dragState.originIndex !== dragState.targetIndex) {
47614
+ const tableInfo = findTableInfo(editor, dragState.anchorPos);
47615
+ const responsiveRatios = tableInfo && normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive" ? moveResponsiveColumnRatio(
47616
+ getTableColumnRatios(tableInfo.node),
47617
+ dragState.originIndex,
47618
+ dragState.targetIndex
47619
+ ) : null;
46906
47620
  moveTableColumn({
46907
47621
  from: dragState.originIndex,
46908
47622
  to: dragState.targetIndex,
46909
47623
  pos: dragState.anchorPos,
46910
47624
  select: true
46911
- })(editor.state, editor.view.dispatch);
47625
+ })(editor.state, (transaction) => {
47626
+ if (tableInfo && responsiveRatios) {
47627
+ const nextTable = transaction.doc.nodeAt(tableInfo.pos);
47628
+ if (nextTable?.type.name === "table") {
47629
+ transaction.setNodeMarkup(tableInfo.pos, void 0, {
47630
+ ...nextTable.attrs,
47631
+ widthMode: "responsive",
47632
+ columnRatios: responsiveRatios
47633
+ });
47634
+ }
47635
+ }
47636
+ editor.view.dispatch(transaction);
47637
+ });
46912
47638
  scheduleSyncFromSelection();
46913
47639
  }
46914
47640
  if (dragState.kind === "add-row") {
@@ -46942,6 +47668,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
46942
47668
  deltaY: event.clientY - dragState.startY,
46943
47669
  lockAxis: event.ctrlKey && !event.shiftKey,
46944
47670
  preserveRatio: event.ctrlKey && event.shiftKey,
47671
+ edge: dragState.edge,
46945
47672
  snapshot: dragState.snapshot
46946
47673
  });
46947
47674
  if (tableResizePreviewFrameRef.current === null) {
@@ -46953,7 +47680,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
46953
47680
  updateTableResizePreview(currentDragState.pendingDimensions);
46954
47681
  });
46955
47682
  }
46956
- setDocumentCursor(editorDocument, "nwse-resize");
47683
+ setDocumentCursor(editorDocument, dragState.edge === "both" ? "nwse-resize" : "ew-resize");
46957
47684
  if (event.cancelable) event.preventDefault();
46958
47685
  };
46959
47686
  const finishTableResize = (event, commit) => {
@@ -46964,7 +47691,12 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
46964
47691
  tableResizePreviewFrameRef.current = null;
46965
47692
  }
46966
47693
  updateTableResizePreview(dragState.pendingDimensions);
46967
- if (commit && applyTableSize(editor, dragState.snapshot, dragState.pendingDimensions)) {
47694
+ if (commit && applyTableSize(
47695
+ editor,
47696
+ dragState.snapshot,
47697
+ dragState.pendingDimensions,
47698
+ { widthMode: dragState.snapshot.widthMode }
47699
+ )) {
46968
47700
  scheduleSyncFromSelection();
46969
47701
  }
46970
47702
  clearDrag();
@@ -47202,6 +47934,7 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
47202
47934
  ctrlHint: t("tableMenu.resizeCtrlHint"),
47203
47935
  frameRef: tableResizeFrameRef,
47204
47936
  layout,
47937
+ onFitWidth: fitTableToEditorWidth,
47205
47938
  onStartResize: startTableResize,
47206
47939
  resizeBothLabel: t("tableMenu.resizeBoth")
47207
47940
  }