@underverse-ui/underverse 2.0.27 → 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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@underverse-ui/underverse",
3
- "version": "2.0.27",
3
+ "version": "2.0.29",
4
4
  "sourceEntry": "src/index.ts",
5
5
  "totalExports": 263,
6
6
  "exports": [
@@ -1198,6 +1198,166 @@ function sanitizeUEditorUrl(raw, kind) {
1198
1198
  import { Extension } from "@tiptap/core";
1199
1199
  import { Plugin } from "@tiptap/pm/state";
1200
1200
 
1201
+ // src/components/UEditor/table-width-model.ts
1202
+ var TABLE_WIDTH_BASIS_POINTS = 1e4;
1203
+ var DEFAULT_RESPONSIVE_TABLE_WIDTH_BP = TABLE_WIDTH_BASIS_POINTS;
1204
+ var MAX_RESPONSIVE_TABLE_WIDTH_BP = 1e5;
1205
+ function positiveNumber(value, fallback) {
1206
+ const number = Number(value);
1207
+ return Number.isFinite(number) && number > 0 ? number : fallback;
1208
+ }
1209
+ function clampTableBasisPoints(value, fallback = TABLE_WIDTH_BASIS_POINTS, maximum = TABLE_WIDTH_BASIS_POINTS) {
1210
+ const number = Number(value);
1211
+ if (!Number.isFinite(number)) return fallback;
1212
+ return Math.min(maximum, Math.max(0, Math.round(number)));
1213
+ }
1214
+ function clampResponsiveTableWidthBp(value, fallback = DEFAULT_RESPONSIVE_TABLE_WIDTH_BP) {
1215
+ return Math.max(1, clampTableBasisPoints(value, fallback, MAX_RESPONSIVE_TABLE_WIDTH_BP));
1216
+ }
1217
+ function normalizeTableWidthMode(value) {
1218
+ return value === "responsive" || value === "full" ? "responsive" : "fixed";
1219
+ }
1220
+ function parsePercentageToBasisPoints(value, maximum = MAX_RESPONSIVE_TABLE_WIDTH_BP) {
1221
+ if (!value) return null;
1222
+ const match = value.trim().match(/^(-?\d+(?:\.\d+)?)%$/);
1223
+ if (!match) return null;
1224
+ const percentage = Number.parseFloat(match[1]);
1225
+ if (!Number.isFinite(percentage)) return null;
1226
+ return clampTableBasisPoints(percentage * 100, TABLE_WIDTH_BASIS_POINTS, maximum);
1227
+ }
1228
+ function formatBasisPointsAsPercentage(value) {
1229
+ const percentage = clampTableBasisPoints(value, 0, MAX_RESPONSIVE_TABLE_WIDTH_BP) / 100;
1230
+ return `${Number.parseFloat(percentage.toFixed(2))}%`;
1231
+ }
1232
+ function parseColumnRatios(value) {
1233
+ const parts = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
1234
+ const ratios = parts.map((part) => Number(part));
1235
+ return ratios.length > 0 && ratios.every((ratio) => Number.isFinite(ratio) && ratio > 0) ? ratios : null;
1236
+ }
1237
+ function normalizeColumnRatios(values, columnCount) {
1238
+ if (columnCount <= 0) return [];
1239
+ const source = values?.length === columnCount ? values.map((value) => positiveNumber(value, 1)) : Array.from({ length: columnCount }, () => 1);
1240
+ const sourceTotal = source.reduce((sum, value) => sum + value, 0);
1241
+ const normalized = source.map((value) => Math.max(1, Math.round(value / sourceTotal * TABLE_WIDTH_BASIS_POINTS)));
1242
+ let difference = TABLE_WIDTH_BASIS_POINTS - normalized.reduce((sum, value) => sum + value, 0);
1243
+ while (difference !== 0) {
1244
+ let changed = false;
1245
+ for (let index = normalized.length - 1; index >= 0 && difference !== 0; index -= 1) {
1246
+ if (difference < 0 && normalized[index] <= 1) continue;
1247
+ normalized[index] += difference > 0 ? 1 : -1;
1248
+ difference += difference > 0 ? -1 : 1;
1249
+ changed = true;
1250
+ }
1251
+ if (!changed) break;
1252
+ }
1253
+ return normalized;
1254
+ }
1255
+ function getLogicalTableColumnCount(table) {
1256
+ const firstRow = table.firstChild;
1257
+ if (!firstRow) return 0;
1258
+ let count = 0;
1259
+ firstRow.forEach((cell) => {
1260
+ count += Math.max(1, Number(cell.attrs.colspan) || 1);
1261
+ });
1262
+ return count;
1263
+ }
1264
+ function getLegacyTableColumnWeights(table, fallback = 100) {
1265
+ const weights = [];
1266
+ const firstRow = table.firstChild;
1267
+ if (!firstRow) return weights;
1268
+ firstRow.forEach((cell) => {
1269
+ const colspan = Math.max(1, Number(cell.attrs.colspan) || 1);
1270
+ const colwidth = Array.isArray(cell.attrs.colwidth) ? cell.attrs.colwidth : [];
1271
+ for (let index = 0; index < colspan; index += 1) {
1272
+ weights.push(positiveNumber(colwidth[index], fallback));
1273
+ }
1274
+ });
1275
+ return weights;
1276
+ }
1277
+ function getTableColumnRatios(table) {
1278
+ const columnCount = getLogicalTableColumnCount(table);
1279
+ const stored = parseColumnRatios(table.attrs.columnRatios);
1280
+ return normalizeColumnRatios(
1281
+ stored?.length === columnCount ? stored : getLegacyTableColumnWeights(table),
1282
+ columnCount
1283
+ );
1284
+ }
1285
+ function getResponsiveTableWidthBp(table) {
1286
+ return clampResponsiveTableWidthBp(table.attrs.widthBp);
1287
+ }
1288
+ function getResponsiveTableOffsetBp(table) {
1289
+ const width = getResponsiveTableWidthBp(table);
1290
+ return resolveResponsiveTableOffsetBp(width, null, table.attrs.offsetBp);
1291
+ }
1292
+ function resolveResponsiveTableOffsetBp(widthBp, tableAlign, fallbackOffsetBp = 0) {
1293
+ const availableGap = Math.max(
1294
+ 0,
1295
+ TABLE_WIDTH_BASIS_POINTS - clampResponsiveTableWidthBp(widthBp)
1296
+ );
1297
+ if (tableAlign === "center") return Math.round(availableGap / 2);
1298
+ if (tableAlign === "right") return availableGap;
1299
+ if (tableAlign === "left") return 0;
1300
+ return Math.min(availableGap, clampTableBasisPoints(fallbackOffsetBp, 0));
1301
+ }
1302
+ function insertResponsiveColumnRatio(ratios, insertIndex, sourceIndex) {
1303
+ if (ratios.length === 0) return [TABLE_WIDTH_BASIS_POINTS];
1304
+ const safeSourceIndex = Math.max(0, Math.min(sourceIndex, ratios.length - 1));
1305
+ const next = [...ratios];
1306
+ const sourceRatio = next[safeSourceIndex];
1307
+ next.splice(Math.max(0, Math.min(insertIndex, next.length)), 0, sourceRatio);
1308
+ return normalizeColumnRatios(next, next.length);
1309
+ }
1310
+ function deleteResponsiveColumnRatio(ratios, deleteIndex) {
1311
+ if (ratios.length <= 1) return [];
1312
+ const next = ratios.filter((_, index) => index !== deleteIndex);
1313
+ return normalizeColumnRatios(next, next.length);
1314
+ }
1315
+ function moveResponsiveColumnRatio(ratios, from, to) {
1316
+ if (from === to || from < 0 || from >= ratios.length || to < 0 || to >= ratios.length) return [...ratios];
1317
+ const next = [...ratios];
1318
+ const [moved] = next.splice(from, 1);
1319
+ next.splice(to, 0, moved);
1320
+ return normalizeColumnRatios(next, next.length);
1321
+ }
1322
+ function insertResponsiveColumnLayout(widthBp, ratios, insertIndex, sourceIndex) {
1323
+ if (ratios.length === 0) {
1324
+ return {
1325
+ widthBp: clampResponsiveTableWidthBp(widthBp),
1326
+ columnRatios: [TABLE_WIDTH_BASIS_POINTS]
1327
+ };
1328
+ }
1329
+ const normalized = normalizeColumnRatios(ratios, ratios.length);
1330
+ const safeSourceIndex = Math.max(0, Math.min(sourceIndex, normalized.length - 1));
1331
+ const sourceRatio = normalized[safeSourceIndex];
1332
+ return {
1333
+ // Growing by the source column's share preserves every existing column's
1334
+ // rendered width instead of squeezing the whole table back into 100%.
1335
+ widthBp: clampResponsiveTableWidthBp(
1336
+ clampResponsiveTableWidthBp(widthBp) * (TABLE_WIDTH_BASIS_POINTS + sourceRatio) / TABLE_WIDTH_BASIS_POINTS
1337
+ ),
1338
+ columnRatios: insertResponsiveColumnRatio(normalized, insertIndex, safeSourceIndex)
1339
+ };
1340
+ }
1341
+ function deleteResponsiveColumnLayout(widthBp, ratios, deleteIndex) {
1342
+ const normalized = normalizeColumnRatios(ratios, ratios.length);
1343
+ if (normalized.length <= 1 || deleteIndex < 0 || deleteIndex >= normalized.length) {
1344
+ return {
1345
+ widthBp: clampResponsiveTableWidthBp(widthBp),
1346
+ columnRatios: normalized.length <= 1 ? [] : normalized
1347
+ };
1348
+ }
1349
+ const remainingRatio = TABLE_WIDTH_BASIS_POINTS - normalized[deleteIndex];
1350
+ const nextWidthBp = clampResponsiveTableWidthBp(
1351
+ clampResponsiveTableWidthBp(widthBp) * remainingRatio / TABLE_WIDTH_BASIS_POINTS
1352
+ );
1353
+ return {
1354
+ // Deletion is the inverse operation: remaining columns keep their rendered
1355
+ // widths while the table gives the removed column's space back.
1356
+ widthBp: Math.abs(nextWidthBp - DEFAULT_RESPONSIVE_TABLE_WIDTH_BP) <= 1 ? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP : nextWidthBp,
1357
+ columnRatios: deleteResponsiveColumnRatio(normalized, deleteIndex)
1358
+ };
1359
+ }
1360
+
1201
1361
  // src/components/UEditor/clipboard-tables.ts
1202
1362
  var DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR = "#ffffff";
1203
1363
  var DEFAULT_HTML_TABLE_TEXT_COLOR = "#000000";
@@ -1829,13 +1989,34 @@ function createNormalizedRowContent(row, columnCount, fillerCellAttrs) {
1829
1989
  }
1830
1990
  return content;
1831
1991
  }
1832
- function createTableContent(rows, minColumnCount = 1, fillerCellAttrs) {
1992
+ function createTableContent(rows, minColumnCount = 1, fillerCellAttrs, layout) {
1833
1993
  const tableRows = rows.filter((row) => row.cells.length > 0);
1834
1994
  if (tableRows.length === 0) return null;
1835
1995
  const { positionedRows, columnCount } = normalizeTableRows(tableRows);
1836
1996
  if (columnCount < minColumnCount) return null;
1997
+ const inferredColumnWeights = Array.from({ length: columnCount }, () => 100);
1998
+ positionedRows.forEach((row) => {
1999
+ row.cells.forEach(({ cell, colspan, startColumn }) => {
2000
+ const width = colspan === 1 ? cell.attrs?.colwidth?.[0] : null;
2001
+ if (typeof width === "number" && Number.isFinite(width) && width > 0) {
2002
+ inferredColumnWeights[startColumn] = width;
2003
+ }
2004
+ });
2005
+ });
2006
+ const columnRatios = normalizeColumnRatios(
2007
+ layout?.columnRatios?.length === columnCount ? layout.columnRatios : inferredColumnWeights,
2008
+ columnCount
2009
+ );
2010
+ const widthBp = clampResponsiveTableWidthBp(layout?.widthBp, DEFAULT_RESPONSIVE_TABLE_WIDTH_BP);
2011
+ const offsetBp = resolveResponsiveTableOffsetBp(widthBp, null, layout?.offsetBp);
1837
2012
  return {
1838
2013
  type: "table",
2014
+ attrs: {
2015
+ widthMode: "responsive",
2016
+ widthBp,
2017
+ offsetBp,
2018
+ columnRatios
2019
+ },
1839
2020
  content: positionedRows.map((row) => ({
1840
2021
  type: "tableRow",
1841
2022
  ...row.attrs ? { attrs: row.attrs } : {},
@@ -1856,9 +2037,22 @@ function getClipboardTableContent(dataTransfer) {
1856
2037
  if (tables.length !== 1 || hasMeaningfulContentOutsideTable(sourceBody)) return null;
1857
2038
  const table = tables[0];
1858
2039
  if (!(table instanceof HTMLTableElement)) return null;
1859
- return createTableContent(getHtmlTableRows(table, styleMap), 1, {
1860
- backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
1861
- });
2040
+ const storedWidthValue = table.getAttribute("data-table-width-bp");
2041
+ const storedOffsetValue = table.getAttribute("data-table-offset-bp");
2042
+ const storedWidthBp = Number(storedWidthValue);
2043
+ const storedOffsetBp = Number(storedOffsetValue);
2044
+ const widthBp = storedWidthValue !== null && Number.isFinite(storedWidthBp) && storedWidthBp > 0 ? storedWidthBp : parsePercentageToBasisPoints(table.getAttribute("data-table-width") ?? table.style.width) ?? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP;
2045
+ const offsetBp = storedOffsetValue !== null && Number.isFinite(storedOffsetBp) && storedOffsetBp >= 0 ? storedOffsetBp : parsePercentageToBasisPoints(table.getAttribute("data-table-offset") ?? table.style.marginLeft) ?? 0;
2046
+ return createTableContent(
2047
+ getHtmlTableRows(table, styleMap),
2048
+ 1,
2049
+ { backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR },
2050
+ {
2051
+ widthBp,
2052
+ offsetBp,
2053
+ columnRatios: parseColumnRatios(table.getAttribute("data-table-column-ratios"))
2054
+ }
2055
+ );
1862
2056
  }
1863
2057
  function parseClipboardTsvRows(text) {
1864
2058
  const rows = [];
@@ -2907,24 +3101,28 @@ function isRowResizeHotspot(cell, clientX, clientY) {
2907
3101
  }
2908
3102
  function getRelativeBoundaryMetrics(surface, table, row, cell) {
2909
3103
  const surfaceRect = surface.getBoundingClientRect();
3104
+ const originLeft = surfaceRect.left + surface.clientLeft;
3105
+ const originTop = surfaceRect.top + surface.clientTop;
2910
3106
  const tableRect = table.getBoundingClientRect();
2911
3107
  const rowRect = row.getBoundingClientRect();
2912
3108
  const cellRect = cell.getBoundingClientRect();
2913
3109
  return {
2914
- left: tableRect.left - surfaceRect.left + surface.scrollLeft,
2915
- top: tableRect.top - surfaceRect.top + surface.scrollTop,
3110
+ left: tableRect.left - originLeft + surface.scrollLeft,
3111
+ top: tableRect.top - originTop + surface.scrollTop,
2916
3112
  width: tableRect.width,
2917
3113
  height: tableRect.height,
2918
- rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
2919
- columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
3114
+ rowBottom: rowRect.bottom - originTop + surface.scrollTop,
3115
+ columnRight: cellRect.right - originLeft + surface.scrollLeft
2920
3116
  };
2921
3117
  }
2922
3118
  function getRelativeCellMetrics(surface, cell) {
2923
3119
  const surfaceRect = surface.getBoundingClientRect();
3120
+ const originLeft = surfaceRect.left + surface.clientLeft;
3121
+ const originTop = surfaceRect.top + surface.clientTop;
2924
3122
  const cellRect = cell.getBoundingClientRect();
2925
3123
  return {
2926
- left: cellRect.left - surfaceRect.left + surface.scrollLeft,
2927
- top: cellRect.top - surfaceRect.top + surface.scrollTop,
3124
+ left: cellRect.left - originLeft + surface.scrollLeft,
3125
+ top: cellRect.top - originTop + surface.scrollTop,
2928
3126
  width: cellRect.width,
2929
3127
  height: cellRect.height
2930
3128
  };
@@ -2937,6 +3135,8 @@ function getRelativeSelectedCellsMetrics(surface) {
2937
3135
  return null;
2938
3136
  }
2939
3137
  const surfaceRect = surface.getBoundingClientRect();
3138
+ const originLeft = surfaceRect.left + surface.clientLeft;
3139
+ const originTop = surfaceRect.top + surface.clientTop;
2940
3140
  let left = Number.POSITIVE_INFINITY;
2941
3141
  let top = Number.POSITIVE_INFINITY;
2942
3142
  let right = Number.NEGATIVE_INFINITY;
@@ -2949,8 +3149,8 @@ function getRelativeSelectedCellsMetrics(surface) {
2949
3149
  bottom = Math.max(bottom, rect.bottom);
2950
3150
  });
2951
3151
  return {
2952
- left: left - surfaceRect.left + surface.scrollLeft,
2953
- top: top - surfaceRect.top + surface.scrollTop,
3152
+ left: left - originLeft + surface.scrollLeft,
3153
+ top: top - originTop + surface.scrollTop,
2954
3154
  width: right - left,
2955
3155
  height: bottom - top
2956
3156
  };
@@ -2980,10 +3180,14 @@ function findTableNodeInfoFromState(state, anchorPos) {
2980
3180
  function applyTableAlignment(editor, tableAlign, anchorPos) {
2981
3181
  const tableInfo = findTableNodeInfoFromState(editor.state, anchorPos);
2982
3182
  if (!tableInfo) return false;
3183
+ const responsive = normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive";
3184
+ const widthBp = getResponsiveTableWidthBp(tableInfo.node);
3185
+ const offsetBp = resolveResponsiveTableOffsetBp(widthBp, tableAlign);
2983
3186
  editor.view.dispatch(
2984
3187
  editor.state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
2985
3188
  ...tableInfo.node.attrs,
2986
- textAlign: tableAlign
3189
+ textAlign: tableAlign,
3190
+ ...responsive ? { offsetBp } : null
2987
3191
  })
2988
3192
  );
2989
3193
  const domAtTable = editor.view.domAtPos(Math.min(tableInfo.pos + 1, editor.state.doc.content.size)).node;
@@ -3000,8 +3204,8 @@ function applyTableAlignment(editor, tableAlign, anchorPos) {
3000
3204
  }
3001
3205
  if (tableAlign) {
3002
3206
  tableElement.setAttribute("data-table-align", tableAlign);
3003
- tableElement.style.marginLeft = tableAlign === "center" || tableAlign === "right" ? "auto" : "0";
3004
- tableElement.style.marginRight = tableAlign === "center" ? "auto" : tableAlign === "right" ? "0" : "auto";
3207
+ tableElement.style.marginLeft = responsive ? formatBasisPointsAsPercentage(offsetBp) : tableAlign === "center" || tableAlign === "right" ? "auto" : "0";
3208
+ tableElement.style.marginRight = responsive ? "auto" : tableAlign === "center" ? "auto" : tableAlign === "right" ? "0" : "auto";
3005
3209
  } else {
3006
3210
  tableElement.removeAttribute("data-table-align");
3007
3211
  tableElement.style.removeProperty("margin-left");
@@ -3258,7 +3462,24 @@ function duplicateTableColumnAt(editor, columnIndex, cellPos) {
3258
3462
  cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
3259
3463
  return rowInfo.node.type.create(rowInfo.node.attrs, cells);
3260
3464
  });
3261
- return tableNode.type.create(tableNode.attrs, rows);
3465
+ const responsiveLayout = normalizeTableWidthMode(tableNode.attrs.widthMode) === "responsive" ? insertResponsiveColumnLayout(
3466
+ getResponsiveTableWidthBp(tableNode),
3467
+ getTableColumnRatios(tableNode),
3468
+ columnIndex + 1,
3469
+ columnIndex
3470
+ ) : null;
3471
+ const tableAttrs = responsiveLayout ? {
3472
+ ...tableNode.attrs,
3473
+ widthMode: "responsive",
3474
+ widthBp: responsiveLayout.widthBp,
3475
+ offsetBp: resolveResponsiveTableOffsetBp(
3476
+ responsiveLayout.widthBp,
3477
+ tableNode.attrs.textAlign,
3478
+ tableNode.attrs.offsetBp
3479
+ ),
3480
+ columnRatios: responsiveLayout.columnRatios
3481
+ } : tableNode.attrs;
3482
+ return tableNode.type.create(tableAttrs, rows);
3262
3483
  });
3263
3484
  }
3264
3485
  function clearTableColumnAt(editor, columnIndex, cellPos) {
@@ -4763,6 +4984,23 @@ export {
4763
4984
  DropdownMenu_default,
4764
4985
  isSafeUEditorUrl,
4765
4986
  sanitizeUEditorUrl,
4987
+ TABLE_WIDTH_BASIS_POINTS,
4988
+ DEFAULT_RESPONSIVE_TABLE_WIDTH_BP,
4989
+ clampResponsiveTableWidthBp,
4990
+ normalizeTableWidthMode,
4991
+ parsePercentageToBasisPoints,
4992
+ formatBasisPointsAsPercentage,
4993
+ parseColumnRatios,
4994
+ normalizeColumnRatios,
4995
+ getLogicalTableColumnCount,
4996
+ getLegacyTableColumnWeights,
4997
+ getTableColumnRatios,
4998
+ getResponsiveTableWidthBp,
4999
+ getResponsiveTableOffsetBp,
5000
+ resolveResponsiveTableOffsetBp,
5001
+ moveResponsiveColumnRatio,
5002
+ insertResponsiveColumnLayout,
5003
+ deleteResponsiveColumnLayout,
4766
5004
  DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
4767
5005
  DEFAULT_UEDITOR_IMAGE_MIME_TYPES,
4768
5006
  ClipboardImages,
@@ -4823,4 +5061,4 @@ export {
4823
5061
  EditorToolbar,
4824
5062
  UEDITOR_PROSEMIRROR_CLASS_NAME
4825
5063
  };
4826
- //# sourceMappingURL=chunk-BDRCOIYR.js.map
5064
+ //# sourceMappingURL=chunk-5QWYO527.js.map