@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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@underverse-ui/underverse",
3
- "version": "2.0.28",
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 = [];
@@ -2986,10 +3180,14 @@ function findTableNodeInfoFromState(state, anchorPos) {
2986
3180
  function applyTableAlignment(editor, tableAlign, anchorPos) {
2987
3181
  const tableInfo = findTableNodeInfoFromState(editor.state, anchorPos);
2988
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);
2989
3186
  editor.view.dispatch(
2990
3187
  editor.state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
2991
3188
  ...tableInfo.node.attrs,
2992
- textAlign: tableAlign
3189
+ textAlign: tableAlign,
3190
+ ...responsive ? { offsetBp } : null
2993
3191
  })
2994
3192
  );
2995
3193
  const domAtTable = editor.view.domAtPos(Math.min(tableInfo.pos + 1, editor.state.doc.content.size)).node;
@@ -3006,8 +3204,8 @@ function applyTableAlignment(editor, tableAlign, anchorPos) {
3006
3204
  }
3007
3205
  if (tableAlign) {
3008
3206
  tableElement.setAttribute("data-table-align", tableAlign);
3009
- tableElement.style.marginLeft = tableAlign === "center" || tableAlign === "right" ? "auto" : "0";
3010
- 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";
3011
3209
  } else {
3012
3210
  tableElement.removeAttribute("data-table-align");
3013
3211
  tableElement.style.removeProperty("margin-left");
@@ -3264,7 +3462,24 @@ function duplicateTableColumnAt(editor, columnIndex, cellPos) {
3264
3462
  cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
3265
3463
  return rowInfo.node.type.create(rowInfo.node.attrs, cells);
3266
3464
  });
3267
- 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);
3268
3483
  });
3269
3484
  }
3270
3485
  function clearTableColumnAt(editor, columnIndex, cellPos) {
@@ -4769,6 +4984,23 @@ export {
4769
4984
  DropdownMenu_default,
4770
4985
  isSafeUEditorUrl,
4771
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,
4772
5004
  DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
4773
5005
  DEFAULT_UEDITOR_IMAGE_MIME_TYPES,
4774
5006
  ClipboardImages,
@@ -4829,4 +5061,4 @@ export {
4829
5061
  EditorToolbar,
4830
5062
  UEDITOR_PROSEMIRROR_CLASS_NAME
4831
5063
  };
4832
- //# sourceMappingURL=chunk-KTOXCD6J.js.map
5064
+ //# sourceMappingURL=chunk-5QWYO527.js.map