@underverse-ui/underverse 1.0.101 → 1.0.103

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
@@ -23278,8 +23278,8 @@ function useLocale2() {
23278
23278
  }
23279
23279
 
23280
23280
  // src/components/UEditor/UEditor.tsx
23281
- var import_react52 = __toESM(require("react"), 1);
23282
- var import_react53 = require("@tiptap/react");
23281
+ var import_react54 = __toESM(require("react"), 1);
23282
+ var import_react55 = require("@tiptap/react");
23283
23283
 
23284
23284
  // src/components/UEditor/extensions.ts
23285
23285
  var import_extension_document = __toESM(require("@tiptap/extension-document"), 1);
@@ -30805,6 +30805,111 @@ var columnResizingPluginKey = new PluginKey3("tableColumnResizing");
30805
30805
 
30806
30806
  // src/components/UEditor/table-controls.tsx
30807
30807
  var import_lucide_react46 = require("lucide-react");
30808
+
30809
+ // src/components/UEditor/table-dom-utils.ts
30810
+ var MIN_TABLE_ROW_HEIGHT = 36;
30811
+ var COLUMN_RESIZE_LINE_THICKNESS = 2;
30812
+ var ROW_RESIZE_LINE_THICKNESS = 2;
30813
+ var UEDITOR_TABLE_LAYOUT_CHANGE_EVENT = "ueditor-table-layout-change";
30814
+ var TABLE_RESIZE_HIT_ZONE = 10;
30815
+ function findTableRowNodeInfo(view, rowElement) {
30816
+ const firstCell = rowElement.querySelector("th,td");
30817
+ if (!firstCell) return null;
30818
+ const cellPos = view.posAtDOM(firstCell, 0);
30819
+ const $pos = view.state.doc.resolve(cellPos);
30820
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
30821
+ const node = $pos.node(depth);
30822
+ if (node.type.name === "tableRow") {
30823
+ return {
30824
+ pos: $pos.before(depth),
30825
+ node
30826
+ };
30827
+ }
30828
+ }
30829
+ return null;
30830
+ }
30831
+ function resolveEventElement(target) {
30832
+ if (target instanceof Element) return target;
30833
+ if (target instanceof Node) return target.parentElement;
30834
+ return null;
30835
+ }
30836
+ function getSelectionTableCell(view) {
30837
+ const browserSelection = window.getSelection();
30838
+ const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
30839
+ const anchorCell = anchorElement?.closest?.("th,td");
30840
+ if (anchorCell instanceof HTMLElement) {
30841
+ return anchorCell;
30842
+ }
30843
+ const { from } = view.state.selection;
30844
+ const domAtPos = view.domAtPos(from);
30845
+ const element = resolveEventElement(domAtPos.node);
30846
+ const cell = element?.closest?.("th,td");
30847
+ return cell instanceof HTMLElement ? cell : null;
30848
+ }
30849
+ function isRowResizeHotspot(cell, clientX, clientY) {
30850
+ const rect = cell.getBoundingClientRect();
30851
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
30852
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
30853
+ return nearBottom && !nearRight;
30854
+ }
30855
+ function isColumnResizeHotspot(cell, clientX, clientY) {
30856
+ const rect = cell.getBoundingClientRect();
30857
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
30858
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
30859
+ return nearRight && !nearBottom;
30860
+ }
30861
+ function getRelativeBoundaryMetrics(surface, table, row, cell) {
30862
+ const surfaceRect = surface.getBoundingClientRect();
30863
+ const tableRect = table.getBoundingClientRect();
30864
+ const rowRect = row.getBoundingClientRect();
30865
+ const cellRect = cell.getBoundingClientRect();
30866
+ return {
30867
+ left: tableRect.left - surfaceRect.left + surface.scrollLeft,
30868
+ top: tableRect.top - surfaceRect.top + surface.scrollTop,
30869
+ width: tableRect.width,
30870
+ height: tableRect.height,
30871
+ rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
30872
+ columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
30873
+ };
30874
+ }
30875
+ function getRelativeCellMetrics(surface, cell) {
30876
+ const surfaceRect = surface.getBoundingClientRect();
30877
+ const cellRect = cell.getBoundingClientRect();
30878
+ return {
30879
+ left: cellRect.left - surfaceRect.left + surface.scrollLeft,
30880
+ top: cellRect.top - surfaceRect.top + surface.scrollTop,
30881
+ width: cellRect.width,
30882
+ height: cellRect.height
30883
+ };
30884
+ }
30885
+ function getRelativeSelectedCellsMetrics(surface) {
30886
+ const selectedCells = Array.from(
30887
+ surface.querySelectorAll("td.selectedCell, th.selectedCell")
30888
+ );
30889
+ if (selectedCells.length === 0) {
30890
+ return null;
30891
+ }
30892
+ const surfaceRect = surface.getBoundingClientRect();
30893
+ let left = Number.POSITIVE_INFINITY;
30894
+ let top = Number.POSITIVE_INFINITY;
30895
+ let right = Number.NEGATIVE_INFINITY;
30896
+ let bottom = Number.NEGATIVE_INFINITY;
30897
+ selectedCells.forEach((cell) => {
30898
+ const rect = cell.getBoundingClientRect();
30899
+ left = Math.min(left, rect.left);
30900
+ top = Math.min(top, rect.top);
30901
+ right = Math.max(right, rect.right);
30902
+ bottom = Math.max(bottom, rect.bottom);
30903
+ });
30904
+ return {
30905
+ left: left - surfaceRect.left + surface.scrollLeft,
30906
+ top: top - surfaceRect.top + surface.scrollTop,
30907
+ width: right - left,
30908
+ height: bottom - top
30909
+ };
30910
+ }
30911
+
30912
+ // src/components/UEditor/table-controls.tsx
30808
30913
  var import_jsx_runtime83 = require("react/jsx-runtime");
30809
30914
  var FALLBACK_TABLE_ROW_HEIGHT = 44;
30810
30915
  var FALLBACK_TABLE_COLUMN_WIDTH = 160;
@@ -31104,6 +31209,7 @@ function TableControls({ editor, containerRef }) {
31104
31209
  surface.addEventListener("mouseover", handleSurfaceMouseMove);
31105
31210
  surface.addEventListener("mousemove", handleSurfaceMouseMove);
31106
31211
  surface.addEventListener("scroll", refreshCurrentLayout, { passive: true });
31212
+ surface.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, refreshCurrentLayout);
31107
31213
  window.addEventListener("resize", refreshCurrentLayout);
31108
31214
  editor.on("selectionUpdate", syncFromSelection);
31109
31215
  editor.on("update", refreshCurrentLayout);
@@ -31115,6 +31221,7 @@ function TableControls({ editor, containerRef }) {
31115
31221
  surface.removeEventListener("mouseover", handleSurfaceMouseMove);
31116
31222
  surface.removeEventListener("mousemove", handleSurfaceMouseMove);
31117
31223
  surface.removeEventListener("scroll", refreshCurrentLayout);
31224
+ surface.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, refreshCurrentLayout);
31118
31225
  window.removeEventListener("resize", refreshCurrentLayout);
31119
31226
  editor.off("selectionUpdate", syncFromSelection);
31120
31227
  editor.off("update", refreshCurrentLayout);
@@ -31798,194 +31905,352 @@ function TableControls({ editor, containerRef }) {
31798
31905
  ] });
31799
31906
  }
31800
31907
 
31801
- // src/components/UEditor/UEditor.tsx
31802
- var import_jsx_runtime84 = require("react/jsx-runtime");
31803
- var TABLE_RESIZE_HIT_ZONE = 10;
31804
- var MIN_TABLE_ROW_HEIGHT = 36;
31805
- var COLUMN_RESIZE_LINE_THICKNESS = 2;
31806
- var ROW_RESIZE_LINE_THICKNESS = 2;
31807
- function applyPreviewRowHeight(rowElement, nextHeight) {
31808
- rowElement.style.height = `${nextHeight}px`;
31809
- rowElement.style.minHeight = `${nextHeight}px`;
31810
- rowElement.querySelectorAll("th,td").forEach((cell) => {
31811
- if (cell instanceof HTMLElement) {
31812
- cell.style.height = `${nextHeight}px`;
31813
- cell.style.minHeight = `${nextHeight}px`;
31908
+ // src/components/UEditor/editor-styles.ts
31909
+ var UEDITOR_PROSEMIRROR_CLASS_NAME = cn(
31910
+ "prose prose-sm sm:prose dark:prose-invert max-w-none",
31911
+ "focus:outline-none",
31912
+ "px-4 py-4",
31913
+ "[&_.is-editor-empty]:before:content-[attr(data-placeholder)]",
31914
+ "[&_.is-editor-empty]:before:text-muted-foreground/50",
31915
+ "[&_.is-editor-empty]:before:float-left",
31916
+ "[&_.is-editor-empty]:before:pointer-events-none",
31917
+ "[&_.is-editor-empty]:before:h-0",
31918
+ "[&_ul[data-type='taskList']]:list-none",
31919
+ "[&_ul[data-type='taskList']]:pl-0",
31920
+ "[&_ul[data-type='taskList']_li]:flex",
31921
+ "[&_ul[data-type='taskList']_li]:items-start",
31922
+ "[&_ul[data-type='taskList']_li]:gap-2",
31923
+ "[&_ul[data-type='taskList']_li>label]:mt-0.5",
31924
+ "[&_ul[data-type='taskList']_li>label>input]:w-4",
31925
+ "[&_ul[data-type='taskList']_li>label>input]:h-4",
31926
+ "[&_ul[data-type='taskList']_li>label>input]:rounded",
31927
+ "[&_ul[data-type='taskList']_li>label>input]:border-2",
31928
+ "[&_ul[data-type='taskList']_li>label>input]:border-primary/50",
31929
+ "[&_ul[data-type='taskList']_li>label>input]:accent-primary",
31930
+ "[&_pre]:bg-muted/40!",
31931
+ "[&_pre]:text-foreground!",
31932
+ "[&_pre]:border!",
31933
+ "[&_pre]:border-border/60!",
31934
+ "[&_pre_code]:bg-transparent!",
31935
+ "[&_.tableWrapper]:overflow-x-auto",
31936
+ "[&_.tableWrapper]:pb-1.5",
31937
+ "[&_.tableWrapper]:select-text",
31938
+ "[&_.tableWrapper]:[scrollbar-width:thin]",
31939
+ "[&_.tableWrapper]:[scrollbar-color:hsl(var(--border))_transparent]",
31940
+ "[&_.tableWrapper::-webkit-scrollbar]:h-2",
31941
+ "[&_.tableWrapper::-webkit-scrollbar]:w-2",
31942
+ "[&_.tableWrapper::-webkit-scrollbar-track]:rounded-full",
31943
+ "[&_.tableWrapper::-webkit-scrollbar-track]:bg-transparent",
31944
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:rounded-full",
31945
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:border",
31946
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:border-solid",
31947
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:border-transparent",
31948
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:bg-border/70",
31949
+ "[&_.tableWrapper::-webkit-scrollbar-thumb:hover]:bg-muted-foreground/45",
31950
+ "[&_table]:table-fixed",
31951
+ "[&_table]:overflow-hidden",
31952
+ "[&_table]:select-text",
31953
+ "[&_table[data-table-align]]:w-max",
31954
+ "[&_table[data-table-align]]:max-w-full",
31955
+ "[&_table[data-table-align='center']]:mx-auto",
31956
+ "[&_table[data-table-align='right']]:ml-auto",
31957
+ "[&_table[data-table-align='right']]:mr-0",
31958
+ "[&_td]:relative",
31959
+ "[&_td]:align-top",
31960
+ "[&_td]:box-border",
31961
+ "[&_td]:select-text",
31962
+ "[&_th]:relative",
31963
+ "[&_th]:align-top",
31964
+ "[&_th]:box-border",
31965
+ "[&_th]:select-text",
31966
+ "[&_.selectedCell]:after:content-['']",
31967
+ "[&_.selectedCell]:after:absolute",
31968
+ "[&_.selectedCell]:after:inset-0",
31969
+ "[&_.selectedCell]:after:z-[2]",
31970
+ "[&_.selectedCell]:after:bg-primary/15",
31971
+ "[&_.selectedCell]:after:pointer-events-none",
31972
+ "[&_.column-resize-handle]:pointer-events-auto",
31973
+ "[&_.column-resize-handle]:cursor-col-resize",
31974
+ "[&_.column-resize-handle]:absolute",
31975
+ "[&_.column-resize-handle]:top-[-1px]",
31976
+ "[&_.column-resize-handle]:bottom-[-1px]",
31977
+ "[&_.column-resize-handle]:right-[-5px]",
31978
+ "[&_.column-resize-handle]:z-10",
31979
+ "[&_.column-resize-handle]:w-2.5",
31980
+ "[&_.column-resize-handle]:bg-transparent",
31981
+ "[&_.column-resize-handle]:rounded-none",
31982
+ "[&_.column-resize-handle]:opacity-0",
31983
+ "[&_.column-resize-handle]:transition-opacity",
31984
+ "[&_.column-resize-handle]:after:absolute",
31985
+ "[&_.column-resize-handle]:after:top-0",
31986
+ "[&_.column-resize-handle]:after:bottom-0",
31987
+ "[&_.column-resize-handle]:after:left-1/2",
31988
+ "[&_.column-resize-handle]:after:w-0.5",
31989
+ "[&_.column-resize-handle]:after:-translate-x-1/2",
31990
+ "[&_.column-resize-handle]:after:rounded-full",
31991
+ "[&_.column-resize-handle]:after:bg-primary/75",
31992
+ "[&_.column-resize-handle]:after:content-['']",
31993
+ "[&.resize-cursor_.column-resize-handle]:opacity-100",
31994
+ "[&.resize-cursor_.column-resize-handle]:after:bg-primary",
31995
+ "[&.resize-cursor]:cursor-col-resize",
31996
+ "[&.resize-row-cursor]:cursor-row-resize",
31997
+ "[&_img.ProseMirror-selectednode]:ring-2",
31998
+ "[&_img.ProseMirror-selectednode]:ring-primary/60",
31999
+ "[&_img.ProseMirror-selectednode]:ring-offset-2",
32000
+ "[&_img.ProseMirror-selectednode]:ring-offset-background",
32001
+ "[&_hr]:border-t-2",
32002
+ "[&_hr]:border-primary/30",
32003
+ "[&_hr]:my-8",
32004
+ "[&_h1]:text-3xl",
32005
+ "[&_h1]:font-bold",
32006
+ "[&_h1]:mt-6",
32007
+ "[&_h1]:mb-4",
32008
+ "[&_h1]:text-foreground",
32009
+ "[&_h2]:text-2xl",
32010
+ "[&_h2]:font-semibold",
32011
+ "[&_h2]:mt-5",
32012
+ "[&_h2]:mb-3",
32013
+ "[&_h2]:text-foreground",
32014
+ "[&_h3]:text-xl",
32015
+ "[&_h3]:font-semibold",
32016
+ "[&_h3]:mt-4",
32017
+ "[&_h3]:mb-2",
32018
+ "[&_h3]:text-foreground",
32019
+ "[&_ul:not([data-type='taskList'])]:list-disc",
32020
+ "[&_ul:not([data-type='taskList'])]:pl-6",
32021
+ "[&_ul:not([data-type='taskList'])]:my-3",
32022
+ "[&_ol]:list-decimal",
32023
+ "[&_ol]:pl-6",
32024
+ "[&_ol]:my-3",
32025
+ "[&_li]:my-1",
32026
+ "[&_li]:pl-1",
32027
+ "[&_li_p]:my-0",
32028
+ "[&_blockquote]:border-l-4",
32029
+ "[&_blockquote]:border-primary",
32030
+ "[&_blockquote]:pl-4",
32031
+ "[&_blockquote]:py-2",
32032
+ "[&_blockquote]:my-4",
32033
+ "[&_blockquote]:bg-muted/30",
32034
+ "[&_blockquote]:rounded-r-lg",
32035
+ "[&_blockquote]:italic",
32036
+ "[&_blockquote]:text-muted-foreground",
32037
+ "[&_blockquote_p]:my-0",
32038
+ "[&_[data-image-layout='left']+p]:mt-1",
32039
+ "[&_[data-image-layout='left']+p]:min-h-[5rem]",
32040
+ "[&_[data-image-layout='right']+p]:mt-1",
32041
+ "[&_[data-image-layout='right']+p]:min-h-[5rem]",
32042
+ "max-md:[&_[data-image-layout='left']]:float-none",
32043
+ "max-md:[&_[data-image-layout='left']]:mr-0",
32044
+ "max-md:[&_[data-image-layout='left']]:ml-0",
32045
+ "max-md:[&_[data-image-layout='left']]:max-w-full",
32046
+ "max-md:[&_[data-image-layout='right']]:float-none",
32047
+ "max-md:[&_[data-image-layout='right']]:mr-0",
32048
+ "max-md:[&_[data-image-layout='right']]:ml-0",
32049
+ "max-md:[&_[data-image-layout='right']]:max-w-full",
32050
+ "max-md:[&_[data-image-layout='left']+p]:min-h-0",
32051
+ "max-md:[&_[data-image-layout='right']+p]:min-h-0"
32052
+ );
32053
+
32054
+ // src/components/UEditor/use-table-interactions.ts
32055
+ var import_react53 = __toESM(require("react"), 1);
32056
+
32057
+ // src/components/UEditor/use-table-row-resize.ts
32058
+ var import_react52 = __toESM(require("react"), 1);
32059
+ function useTableRowResize({
32060
+ editor,
32061
+ setHoveredTableCell,
32062
+ clearHoveredTableCell,
32063
+ showRowGuide,
32064
+ clearAllTableResizeHover,
32065
+ scheduleTableLayoutSync
32066
+ }) {
32067
+ const commitFrameRef = (0, import_react52.useRef)(null);
32068
+ const stateRef = (0, import_react52.useRef)(null);
32069
+ const commitPreview = import_react52.default.useCallback(() => {
32070
+ if (!editor) return;
32071
+ const state = stateRef.current;
32072
+ if (!state) return;
32073
+ const nextHeight = state.pendingHeight;
32074
+ if (nextHeight === state.previewHeight) {
32075
+ document.body.style.cursor = "row-resize";
32076
+ showRowGuide(state.tableElement, state.rowElement, state.cellElement);
32077
+ scheduleTableLayoutSync();
32078
+ return;
31814
32079
  }
31815
- });
31816
- }
31817
- function clearPreviewRowHeight(rowElement) {
31818
- rowElement.style.height = "";
31819
- rowElement.style.minHeight = "";
31820
- rowElement.querySelectorAll("th,td").forEach((cell) => {
31821
- if (cell instanceof HTMLElement) {
31822
- cell.style.height = "";
31823
- cell.style.minHeight = "";
32080
+ state.previewHeight = nextHeight;
32081
+ const tr = editor.view.state.tr;
32082
+ tr.setNodeMarkup(state.rowPos, void 0, {
32083
+ ...state.rowNode.attrs,
32084
+ rowHeight: nextHeight
32085
+ });
32086
+ tr.setMeta("addToHistory", false);
32087
+ editor.view.dispatch(tr);
32088
+ state.rowNode = editor.view.state.doc.nodeAt(state.rowPos) ?? state.rowNode;
32089
+ const refreshedRow = state.tableElement.rows.item(state.rowElement.rowIndex);
32090
+ if (refreshedRow instanceof HTMLTableRowElement) {
32091
+ state.rowElement = refreshedRow;
32092
+ const refreshedCell = refreshedRow.cells.item(state.cellIndex);
32093
+ if (refreshedCell instanceof HTMLTableCellElement) {
32094
+ state.cellElement = refreshedCell;
32095
+ }
32096
+ }
32097
+ document.body.style.cursor = "row-resize";
32098
+ showRowGuide(state.tableElement, state.rowElement, state.cellElement);
32099
+ scheduleTableLayoutSync();
32100
+ }, [editor, scheduleTableLayoutSync, showRowGuide]);
32101
+ const scheduleCommit = import_react52.default.useCallback(() => {
32102
+ if (commitFrameRef.current !== null) return;
32103
+ commitFrameRef.current = window.requestAnimationFrame(() => {
32104
+ commitFrameRef.current = null;
32105
+ commitPreview();
32106
+ });
32107
+ }, [commitPreview]);
32108
+ const syncActiveGuide = import_react52.default.useCallback(() => {
32109
+ const state = stateRef.current;
32110
+ if (!state) return false;
32111
+ setHoveredTableCell(state.cellElement);
32112
+ showRowGuide(state.tableElement, state.rowElement, state.cellElement);
32113
+ return true;
32114
+ }, [setHoveredTableCell, showRowGuide]);
32115
+ const isResizing = import_react52.default.useCallback(() => stateRef.current !== null, []);
32116
+ const beginResize = import_react52.default.useCallback((event, table, row, cell) => {
32117
+ if (!editor || !isRowResizeHotspot(cell, event.clientX, event.clientY)) {
32118
+ return false;
31824
32119
  }
31825
- });
31826
- }
31827
- function findTableRowNodeInfo(view, rowElement) {
31828
- const firstCell = rowElement.querySelector("th,td");
31829
- if (!firstCell) return null;
31830
- const cellPos = view.posAtDOM(firstCell, 0);
31831
- const $pos = view.state.doc.resolve(cellPos);
31832
- for (let depth = $pos.depth; depth > 0; depth -= 1) {
31833
- const node = $pos.node(depth);
31834
- if (node.type.name === "tableRow") {
31835
- return {
31836
- pos: $pos.before(depth),
31837
- node
31838
- };
32120
+ setHoveredTableCell(cell);
32121
+ const rowInfo = findTableRowNodeInfo(editor.view, row);
32122
+ if (!rowInfo) return false;
32123
+ const startHeight = row.getBoundingClientRect().height;
32124
+ stateRef.current = {
32125
+ rowElement: row,
32126
+ tableElement: table,
32127
+ cellElement: cell,
32128
+ cellIndex: cell.cellIndex,
32129
+ rowPos: rowInfo.pos,
32130
+ rowNode: rowInfo.node,
32131
+ startY: event.clientY,
32132
+ startHeight,
32133
+ previewHeight: startHeight,
32134
+ pendingHeight: startHeight
32135
+ };
32136
+ showRowGuide(table, row, cell);
32137
+ document.body.style.cursor = "row-resize";
32138
+ event.preventDefault();
32139
+ event.stopPropagation();
32140
+ return true;
32141
+ }, [editor, setHoveredTableCell, showRowGuide]);
32142
+ const handlePointerMove = import_react52.default.useCallback((event) => {
32143
+ const state = stateRef.current;
32144
+ if (!state) return;
32145
+ const nextHeight = Math.max(
32146
+ MIN_TABLE_ROW_HEIGHT,
32147
+ Math.round(state.startHeight + (event.clientY - state.startY))
32148
+ );
32149
+ if (nextHeight === state.pendingHeight) {
32150
+ document.body.style.cursor = "row-resize";
32151
+ showRowGuide(state.tableElement, state.rowElement, state.cellElement);
32152
+ return;
31839
32153
  }
31840
- }
31841
- return null;
31842
- }
31843
- function resolveEventElement(target) {
31844
- if (target instanceof Element) return target;
31845
- if (target instanceof Node) return target.parentElement;
31846
- return null;
31847
- }
31848
- function getSelectionTableCell(view) {
31849
- const browserSelection = window.getSelection();
31850
- const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
31851
- const anchorCell = anchorElement?.closest?.("th,td");
31852
- if (anchorCell instanceof HTMLElement) {
31853
- return anchorCell;
31854
- }
31855
- const { from } = view.state.selection;
31856
- const domAtPos = view.domAtPos(from);
31857
- const element = resolveEventElement(domAtPos.node);
31858
- const cell = element?.closest?.("th,td");
31859
- return cell instanceof HTMLElement ? cell : null;
31860
- }
31861
- function isRowResizeHotspot(cell, clientX, clientY) {
31862
- const rect = cell.getBoundingClientRect();
31863
- const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
31864
- const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
31865
- return nearBottom && !nearRight;
31866
- }
31867
- function isColumnResizeHotspot(cell, clientX, clientY) {
31868
- const rect = cell.getBoundingClientRect();
31869
- const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
31870
- const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
31871
- return nearRight && !nearBottom;
31872
- }
31873
- function getRelativeBoundaryMetrics(surface, table, row, cell) {
31874
- const surfaceRect = surface.getBoundingClientRect();
31875
- const tableRect = table.getBoundingClientRect();
31876
- const rowRect = row.getBoundingClientRect();
31877
- const cellRect = cell.getBoundingClientRect();
31878
- return {
31879
- left: tableRect.left - surfaceRect.left + surface.scrollLeft,
31880
- top: tableRect.top - surfaceRect.top + surface.scrollTop,
31881
- width: tableRect.width,
31882
- height: tableRect.height,
31883
- rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
31884
- columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
31885
- };
31886
- }
31887
- function getRelativeCellMetrics(surface, cell) {
31888
- const surfaceRect = surface.getBoundingClientRect();
31889
- const cellRect = cell.getBoundingClientRect();
31890
- return {
31891
- left: cellRect.left - surfaceRect.left + surface.scrollLeft,
31892
- top: cellRect.top - surfaceRect.top + surface.scrollTop,
31893
- width: cellRect.width,
31894
- height: cellRect.height
31895
- };
31896
- }
31897
- function getRelativeSelectedCellsMetrics(surface) {
31898
- const selectedCells = Array.from(
31899
- surface.querySelectorAll("td.selectedCell, th.selectedCell")
31900
- );
31901
- if (selectedCells.length === 0) {
31902
- return null;
31903
- }
31904
- const surfaceRect = surface.getBoundingClientRect();
31905
- let left = Number.POSITIVE_INFINITY;
31906
- let top = Number.POSITIVE_INFINITY;
31907
- let right = Number.NEGATIVE_INFINITY;
31908
- let bottom = Number.NEGATIVE_INFINITY;
31909
- selectedCells.forEach((cell) => {
31910
- const rect = cell.getBoundingClientRect();
31911
- left = Math.min(left, rect.left);
31912
- top = Math.min(top, rect.top);
31913
- right = Math.max(right, rect.right);
31914
- bottom = Math.max(bottom, rect.bottom);
31915
- });
32154
+ state.pendingHeight = nextHeight;
32155
+ document.body.style.cursor = "row-resize";
32156
+ scheduleCommit();
32157
+ }, [scheduleCommit, showRowGuide]);
32158
+ const handlePointerUp = import_react52.default.useCallback((event) => {
32159
+ if (!editor) return;
32160
+ const state = stateRef.current;
32161
+ if (!state) return;
32162
+ const nextHeight = Math.max(
32163
+ MIN_TABLE_ROW_HEIGHT,
32164
+ Math.round(state.startHeight + (event.clientY - state.startY))
32165
+ );
32166
+ state.pendingHeight = nextHeight;
32167
+ if (commitFrameRef.current !== null) {
32168
+ window.cancelAnimationFrame(commitFrameRef.current);
32169
+ commitFrameRef.current = null;
32170
+ }
32171
+ commitPreview();
32172
+ const latestState = stateRef.current ?? state;
32173
+ const rowNode = editor.view.state.doc.nodeAt(latestState.rowPos) ?? latestState.rowNode;
32174
+ if (rowNode.attrs.rowHeight !== nextHeight) {
32175
+ const tr = editor.view.state.tr;
32176
+ tr.setNodeMarkup(latestState.rowPos, void 0, {
32177
+ ...rowNode.attrs,
32178
+ rowHeight: nextHeight
32179
+ });
32180
+ editor.view.dispatch(tr);
32181
+ }
32182
+ stateRef.current = null;
32183
+ document.body.style.cursor = "";
32184
+ clearHoveredTableCell();
32185
+ clearAllTableResizeHover();
32186
+ scheduleTableLayoutSync();
32187
+ }, [clearAllTableResizeHover, clearHoveredTableCell, commitPreview, editor, scheduleTableLayoutSync]);
32188
+ const cancelResize = import_react52.default.useCallback(() => {
32189
+ if (!stateRef.current) return;
32190
+ if (commitFrameRef.current !== null) {
32191
+ window.cancelAnimationFrame(commitFrameRef.current);
32192
+ commitFrameRef.current = null;
32193
+ }
32194
+ stateRef.current = null;
32195
+ document.body.style.cursor = "";
32196
+ clearHoveredTableCell();
32197
+ clearAllTableResizeHover();
32198
+ scheduleTableLayoutSync();
32199
+ }, [clearAllTableResizeHover, clearHoveredTableCell, scheduleTableLayoutSync]);
32200
+ const cleanup = import_react52.default.useCallback(() => {
32201
+ if (commitFrameRef.current !== null) {
32202
+ window.cancelAnimationFrame(commitFrameRef.current);
32203
+ commitFrameRef.current = null;
32204
+ }
32205
+ stateRef.current = null;
32206
+ document.body.style.cursor = "";
32207
+ }, []);
31916
32208
  return {
31917
- left: left - surfaceRect.left + surface.scrollLeft,
31918
- top: top - surfaceRect.top + surface.scrollTop,
31919
- width: right - left,
31920
- height: bottom - top
32209
+ beginResize,
32210
+ cancelResize,
32211
+ cleanup,
32212
+ handlePointerMove,
32213
+ handlePointerUp,
32214
+ isResizing,
32215
+ syncActiveGuide
31921
32216
  };
31922
32217
  }
31923
- var UEditor = import_react52.default.forwardRef(({
31924
- content = "",
31925
- onChange,
31926
- onHtmlChange,
31927
- onJsonChange,
31928
- uploadImage,
31929
- uploadImageForSave,
31930
- uploadImageConcurrency = 3,
31931
- imageInsertMode = "base64",
31932
- maxImageFileSize,
31933
- allowedImageMimeTypes,
31934
- fallbackToDataUrl,
31935
- placeholder,
31936
- className,
31937
- editable = true,
31938
- autofocus = false,
31939
- showToolbar = true,
31940
- showBubbleMenu = true,
31941
- showFloatingMenu = false,
31942
- showCharacterCount = true,
31943
- maxCharacters,
31944
- minHeight = "200px",
31945
- maxHeight = "auto",
31946
- variant = "default",
31947
- fontFamilies,
31948
- fontSizes,
31949
- lineHeights,
31950
- letterSpacings
31951
- }, ref) => {
31952
- const t = useSmartTranslations("UEditor");
31953
- const effectivePlaceholder = placeholder ?? t("placeholder");
31954
- const inFlightPrepareRef = (0, import_react52.useRef)(null);
31955
- const lastAppliedContentRef = (0, import_react52.useRef)(content ?? "");
31956
- const editorContentRef = (0, import_react52.useRef)(null);
31957
- const tableColumnGuideRef = (0, import_react52.useRef)(null);
31958
- const tableRowGuideRef = (0, import_react52.useRef)(null);
31959
- const activeTableCellHighlightRef = (0, import_react52.useRef)(null);
31960
- const hoveredTableCellRef = (0, import_react52.useRef)(null);
31961
- const activeTableCellRef = (0, import_react52.useRef)(null);
31962
- const rowResizeStateRef = (0, import_react52.useRef)(null);
31963
- const setEditorResizeCursor = import_react52.default.useCallback((cursor) => {
32218
+
32219
+ // src/components/UEditor/use-table-interactions.ts
32220
+ function useUEditorTableInteractions(editor) {
32221
+ const editorContentRef = (0, import_react53.useRef)(null);
32222
+ const tableColumnGuideRef = (0, import_react53.useRef)(null);
32223
+ const tableRowGuideRef = (0, import_react53.useRef)(null);
32224
+ const activeTableCellHighlightRef = (0, import_react53.useRef)(null);
32225
+ const hoveredTableCellRef = (0, import_react53.useRef)(null);
32226
+ const activeTableCellRef = (0, import_react53.useRef)(null);
32227
+ const tableLayoutSyncFrameRef = (0, import_react53.useRef)(null);
32228
+ const setEditorResizeCursor = import_react53.default.useCallback((cursor) => {
31964
32229
  const proseMirror = editorContentRef.current?.querySelector(".ProseMirror");
31965
32230
  if (proseMirror) {
31966
32231
  proseMirror.style.cursor = cursor;
31967
32232
  }
31968
32233
  }, []);
31969
- const hideColumnGuide = import_react52.default.useCallback(() => {
32234
+ const hideColumnGuide = import_react53.default.useCallback(() => {
31970
32235
  editorContentRef.current?.classList.remove("resize-cursor");
31971
32236
  const guide = tableColumnGuideRef.current;
31972
32237
  if (guide) {
31973
32238
  guide.style.opacity = "0";
31974
32239
  }
31975
32240
  }, []);
31976
- const hideRowGuide = import_react52.default.useCallback(() => {
32241
+ const hideRowGuide = import_react53.default.useCallback(() => {
31977
32242
  editorContentRef.current?.classList.remove("resize-row-cursor");
31978
32243
  const guide = tableRowGuideRef.current;
31979
32244
  if (guide) {
31980
32245
  guide.style.opacity = "0";
31981
32246
  }
31982
32247
  }, []);
31983
- const clearAllTableResizeHover = import_react52.default.useCallback(() => {
32248
+ const clearAllTableResizeHover = import_react53.default.useCallback(() => {
31984
32249
  setEditorResizeCursor("");
31985
32250
  hideColumnGuide();
31986
32251
  hideRowGuide();
31987
32252
  }, [hideColumnGuide, hideRowGuide, setEditorResizeCursor]);
31988
- const updateActiveCellHighlight = import_react52.default.useCallback((cell) => {
32253
+ const updateActiveCellHighlight = import_react53.default.useCallback((cell) => {
31989
32254
  const surface = editorContentRef.current;
31990
32255
  const highlight = activeTableCellHighlightRef.current;
31991
32256
  if (!highlight) return;
@@ -32000,22 +32265,30 @@ var UEditor = import_react52.default.forwardRef(({
32000
32265
  highlight.style.width = `${metrics.width}px`;
32001
32266
  highlight.style.height = `${metrics.height}px`;
32002
32267
  }, []);
32003
- const setActiveTableCell = import_react52.default.useCallback((cell) => {
32268
+ const scheduleTableLayoutSync = import_react53.default.useCallback(() => {
32269
+ if (tableLayoutSyncFrameRef.current !== null) return;
32270
+ tableLayoutSyncFrameRef.current = window.requestAnimationFrame(() => {
32271
+ tableLayoutSyncFrameRef.current = null;
32272
+ updateActiveCellHighlight(activeTableCellRef.current);
32273
+ editorContentRef.current?.dispatchEvent(new CustomEvent(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT));
32274
+ });
32275
+ }, [updateActiveCellHighlight]);
32276
+ const setActiveTableCell = import_react53.default.useCallback((cell) => {
32004
32277
  if (activeTableCellRef.current === cell) return;
32005
32278
  activeTableCellRef.current = cell;
32006
32279
  updateActiveCellHighlight(activeTableCellRef.current);
32007
32280
  }, [updateActiveCellHighlight]);
32008
- const clearActiveTableCell = import_react52.default.useCallback(() => {
32281
+ const clearActiveTableCell = import_react53.default.useCallback(() => {
32009
32282
  activeTableCellRef.current = null;
32010
32283
  updateActiveCellHighlight(null);
32011
32284
  }, [updateActiveCellHighlight]);
32012
- const setHoveredTableCell = import_react52.default.useCallback((cell) => {
32285
+ const setHoveredTableCell = import_react53.default.useCallback((cell) => {
32013
32286
  hoveredTableCellRef.current = cell;
32014
32287
  }, []);
32015
- const clearHoveredTableCell = import_react52.default.useCallback(() => {
32288
+ const clearHoveredTableCell = import_react53.default.useCallback(() => {
32016
32289
  hoveredTableCellRef.current = null;
32017
32290
  }, []);
32018
- const showColumnGuide = import_react52.default.useCallback((table, row, cell) => {
32291
+ const showColumnGuide = import_react53.default.useCallback((table, row, cell) => {
32019
32292
  const surface = editorContentRef.current;
32020
32293
  const guide = tableColumnGuideRef.current;
32021
32294
  if (!surface || !guide) return;
@@ -32028,7 +32301,7 @@ var UEditor = import_react52.default.forwardRef(({
32028
32301
  surface.classList.add("resize-cursor");
32029
32302
  setEditorResizeCursor("col-resize");
32030
32303
  }, [setEditorResizeCursor]);
32031
- const showRowGuide = import_react52.default.useCallback((table, row, cell) => {
32304
+ const showRowGuide = import_react53.default.useCallback((table, row, cell) => {
32032
32305
  const surface = editorContentRef.current;
32033
32306
  const guide = tableRowGuideRef.current;
32034
32307
  if (!surface || !guide) return;
@@ -32041,240 +32314,27 @@ var UEditor = import_react52.default.forwardRef(({
32041
32314
  surface.classList.add("resize-row-cursor");
32042
32315
  setEditorResizeCursor("row-resize");
32043
32316
  }, [setEditorResizeCursor]);
32044
- const extensions = (0, import_react52.useMemo)(
32045
- () => buildUEditorExtensions({
32046
- placeholder: effectivePlaceholder,
32047
- translate: t,
32048
- maxCharacters,
32049
- uploadImage,
32050
- imageInsertMode,
32051
- maxImageFileSize,
32052
- allowedImageMimeTypes,
32053
- fallbackToDataUrl,
32054
- editable
32055
- }),
32056
- [effectivePlaceholder, t, maxCharacters, uploadImage, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable]
32057
- );
32058
- const editor = (0, import_react53.useEditor)({
32059
- immediatelyRender: false,
32060
- extensions,
32061
- content,
32062
- editable,
32063
- autofocus,
32064
- editorProps: {
32065
- handleDOMEvents: {
32066
- keydown: (_view, event) => {
32067
- if (!(event instanceof KeyboardEvent)) return false;
32068
- if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
32069
- event.stopPropagation();
32070
- }
32071
- return false;
32072
- },
32073
- click: (view, event) => {
32074
- if (!(event instanceof MouseEvent)) return false;
32075
- if (event.button !== 0) return false;
32076
- const target = resolveEventElement(event.target);
32077
- const anchor = target?.closest?.("a[href]");
32078
- const href = anchor?.getAttribute("href") ?? "";
32079
- if (!href) return false;
32080
- if (!view.state.selection.empty) return false;
32081
- event.preventDefault();
32082
- event.stopPropagation();
32083
- window.open(href, "_blank", "noopener,noreferrer");
32084
- return true;
32085
- }
32086
- },
32087
- attributes: {
32088
- class: cn(
32089
- "prose prose-sm sm:prose dark:prose-invert max-w-none",
32090
- "focus:outline-none",
32091
- "px-4 py-4",
32092
- "[&_.is-editor-empty]:before:content-[attr(data-placeholder)]",
32093
- "[&_.is-editor-empty]:before:text-muted-foreground/50",
32094
- "[&_.is-editor-empty]:before:float-left",
32095
- "[&_.is-editor-empty]:before:pointer-events-none",
32096
- "[&_.is-editor-empty]:before:h-0",
32097
- "[&_ul[data-type='taskList']]:list-none",
32098
- "[&_ul[data-type='taskList']]:pl-0",
32099
- "[&_ul[data-type='taskList']_li]:flex",
32100
- "[&_ul[data-type='taskList']_li]:items-start",
32101
- "[&_ul[data-type='taskList']_li]:gap-2",
32102
- "[&_ul[data-type='taskList']_li>label]:mt-0.5",
32103
- "[&_ul[data-type='taskList']_li>label>input]:w-4",
32104
- "[&_ul[data-type='taskList']_li>label>input]:h-4",
32105
- "[&_ul[data-type='taskList']_li>label>input]:rounded",
32106
- "[&_ul[data-type='taskList']_li>label>input]:border-2",
32107
- "[&_ul[data-type='taskList']_li>label>input]:border-primary/50",
32108
- "[&_ul[data-type='taskList']_li>label>input]:accent-primary",
32109
- "[&_pre]:bg-muted/40!",
32110
- "[&_pre]:text-foreground!",
32111
- "[&_pre]:border!",
32112
- "[&_pre]:border-border/60!",
32113
- "[&_pre_code]:bg-transparent!",
32114
- "[&_.tableWrapper]:overflow-x-auto",
32115
- "[&_.tableWrapper]:pb-1.5",
32116
- "[&_.tableWrapper]:select-text",
32117
- "[&_.tableWrapper]:[scrollbar-width:thin]",
32118
- "[&_.tableWrapper]:[scrollbar-color:hsl(var(--border))_transparent]",
32119
- "[&_.tableWrapper::-webkit-scrollbar]:h-2",
32120
- "[&_.tableWrapper::-webkit-scrollbar]:w-2",
32121
- "[&_.tableWrapper::-webkit-scrollbar-track]:rounded-full",
32122
- "[&_.tableWrapper::-webkit-scrollbar-track]:bg-transparent",
32123
- "[&_.tableWrapper::-webkit-scrollbar-thumb]:rounded-full",
32124
- "[&_.tableWrapper::-webkit-scrollbar-thumb]:border",
32125
- "[&_.tableWrapper::-webkit-scrollbar-thumb]:border-solid",
32126
- "[&_.tableWrapper::-webkit-scrollbar-thumb]:border-transparent",
32127
- "[&_.tableWrapper::-webkit-scrollbar-thumb]:bg-border/70",
32128
- "[&_.tableWrapper::-webkit-scrollbar-thumb:hover]:bg-muted-foreground/45",
32129
- "[&_table]:table-fixed",
32130
- "[&_table]:overflow-hidden",
32131
- "[&_table]:select-text",
32132
- "[&_table[data-table-align]]:w-max",
32133
- "[&_table[data-table-align]]:max-w-full",
32134
- "[&_table[data-table-align='center']]:mx-auto",
32135
- "[&_table[data-table-align='right']]:ml-auto",
32136
- "[&_table[data-table-align='right']]:mr-0",
32137
- "[&_td]:relative",
32138
- "[&_td]:align-top",
32139
- "[&_td]:box-border",
32140
- "[&_td]:select-text",
32141
- "[&_th]:relative",
32142
- "[&_th]:align-top",
32143
- "[&_th]:box-border",
32144
- "[&_th]:select-text",
32145
- "[&_.selectedCell]:after:content-['']",
32146
- "[&_.selectedCell]:after:absolute",
32147
- "[&_.selectedCell]:after:inset-0",
32148
- "[&_.selectedCell]:after:z-[2]",
32149
- "[&_.selectedCell]:after:bg-primary/15",
32150
- "[&_.selectedCell]:after:pointer-events-none",
32151
- "[&_.column-resize-handle]:pointer-events-auto",
32152
- "[&_.column-resize-handle]:cursor-col-resize",
32153
- "[&_.column-resize-handle]:absolute",
32154
- "[&_.column-resize-handle]:top-[-1px]",
32155
- "[&_.column-resize-handle]:bottom-[-1px]",
32156
- "[&_.column-resize-handle]:right-[-5px]",
32157
- "[&_.column-resize-handle]:z-10",
32158
- "[&_.column-resize-handle]:w-2.5",
32159
- "[&_.column-resize-handle]:bg-transparent",
32160
- "[&_.column-resize-handle]:rounded-none",
32161
- "[&_.column-resize-handle]:opacity-0",
32162
- "[&_.column-resize-handle]:transition-opacity",
32163
- "[&_.column-resize-handle]:after:absolute",
32164
- "[&_.column-resize-handle]:after:top-0",
32165
- "[&_.column-resize-handle]:after:bottom-0",
32166
- "[&_.column-resize-handle]:after:left-1/2",
32167
- "[&_.column-resize-handle]:after:w-0.5",
32168
- "[&_.column-resize-handle]:after:-translate-x-1/2",
32169
- "[&_.column-resize-handle]:after:rounded-full",
32170
- "[&_.column-resize-handle]:after:bg-primary/75",
32171
- "[&_.column-resize-handle]:after:content-['']",
32172
- "[&.resize-cursor_.column-resize-handle]:opacity-100",
32173
- "[&.resize-cursor_.column-resize-handle]:after:bg-primary",
32174
- "[&.resize-cursor]:cursor-col-resize",
32175
- "[&.resize-row-cursor]:cursor-row-resize",
32176
- "[&_img.ProseMirror-selectednode]:ring-2",
32177
- "[&_img.ProseMirror-selectednode]:ring-primary/60",
32178
- "[&_img.ProseMirror-selectednode]:ring-offset-2",
32179
- "[&_img.ProseMirror-selectednode]:ring-offset-background",
32180
- "[&_hr]:border-t-2",
32181
- "[&_hr]:border-primary/30",
32182
- "[&_hr]:my-8",
32183
- "[&_h1]:text-3xl",
32184
- "[&_h1]:font-bold",
32185
- "[&_h1]:mt-6",
32186
- "[&_h1]:mb-4",
32187
- "[&_h1]:text-foreground",
32188
- "[&_h2]:text-2xl",
32189
- "[&_h2]:font-semibold",
32190
- "[&_h2]:mt-5",
32191
- "[&_h2]:mb-3",
32192
- "[&_h2]:text-foreground",
32193
- "[&_h3]:text-xl",
32194
- "[&_h3]:font-semibold",
32195
- "[&_h3]:mt-4",
32196
- "[&_h3]:mb-2",
32197
- "[&_h3]:text-foreground",
32198
- "[&_ul:not([data-type='taskList'])]:list-disc",
32199
- "[&_ul:not([data-type='taskList'])]:pl-6",
32200
- "[&_ul:not([data-type='taskList'])]:my-3",
32201
- "[&_ol]:list-decimal",
32202
- "[&_ol]:pl-6",
32203
- "[&_ol]:my-3",
32204
- "[&_li]:my-1",
32205
- "[&_li]:pl-1",
32206
- "[&_li_p]:my-0",
32207
- "[&_blockquote]:border-l-4",
32208
- "[&_blockquote]:border-primary",
32209
- "[&_blockquote]:pl-4",
32210
- "[&_blockquote]:py-2",
32211
- "[&_blockquote]:my-4",
32212
- "[&_blockquote]:bg-muted/30",
32213
- "[&_blockquote]:rounded-r-lg",
32214
- "[&_blockquote]:italic",
32215
- "[&_blockquote]:text-muted-foreground",
32216
- "[&_blockquote_p]:my-0",
32217
- "[&_[data-image-layout='left']+p]:mt-1",
32218
- "[&_[data-image-layout='left']+p]:min-h-[5rem]",
32219
- "[&_[data-image-layout='right']+p]:mt-1",
32220
- "[&_[data-image-layout='right']+p]:min-h-[5rem]",
32221
- "max-md:[&_[data-image-layout='left']]:float-none",
32222
- "max-md:[&_[data-image-layout='left']]:mr-0",
32223
- "max-md:[&_[data-image-layout='left']]:ml-0",
32224
- "max-md:[&_[data-image-layout='left']]:max-w-full",
32225
- "max-md:[&_[data-image-layout='right']]:float-none",
32226
- "max-md:[&_[data-image-layout='right']]:mr-0",
32227
- "max-md:[&_[data-image-layout='right']]:ml-0",
32228
- "max-md:[&_[data-image-layout='right']]:max-w-full",
32229
- "max-md:[&_[data-image-layout='left']+p]:min-h-0",
32230
- "max-md:[&_[data-image-layout='right']+p]:min-h-0"
32231
- )
32232
- }
32233
- },
32234
- onUpdate: ({ editor: editor2 }) => {
32235
- const html = editor2.getHTML();
32236
- onChange?.(html);
32237
- onHtmlChange?.(html);
32238
- onJsonChange?.(editor2.getJSON());
32239
- }
32317
+ const {
32318
+ beginResize,
32319
+ cancelResize,
32320
+ cleanup: cleanupRowResize,
32321
+ handlePointerMove: handleRowResizePointerMove,
32322
+ handlePointerUp: handleRowResizePointerUp,
32323
+ isResizing: isRowResizing,
32324
+ syncActiveGuide: syncActiveRowResizeGuide
32325
+ } = useTableRowResize({
32326
+ editor,
32327
+ setHoveredTableCell,
32328
+ clearHoveredTableCell,
32329
+ showRowGuide,
32330
+ clearAllTableResizeHover,
32331
+ scheduleTableLayoutSync
32240
32332
  });
32241
- const syncActiveTableCellFromSelection = import_react52.default.useCallback(() => {
32333
+ const syncActiveTableCellFromSelection = import_react53.default.useCallback(() => {
32242
32334
  if (!editor) return;
32243
32335
  setActiveTableCell(getSelectionTableCell(editor.view));
32244
32336
  }, [editor, setActiveTableCell]);
32245
- (0, import_react52.useImperativeHandle)(
32246
- ref,
32247
- () => ({
32248
- prepareContentForSave: async ({ throwOnError = false } = {}) => {
32249
- if (!inFlightPrepareRef.current) {
32250
- const htmlSnapshot = editor?.getHTML() ?? content ?? "";
32251
- inFlightPrepareRef.current = prepareUEditorContentForSave({
32252
- html: htmlSnapshot,
32253
- uploadImageForSave,
32254
- uploadConcurrency: uploadImageConcurrency
32255
- }).finally(() => {
32256
- inFlightPrepareRef.current = null;
32257
- });
32258
- }
32259
- const result = await inFlightPrepareRef.current;
32260
- if (throwOnError && result.errors.length > 0) {
32261
- throw new UEditorPrepareContentForSaveError(result);
32262
- }
32263
- return result;
32264
- }
32265
- }),
32266
- [content, editor, uploadImageForSave, uploadImageConcurrency]
32267
- );
32268
- (0, import_react52.useEffect)(() => {
32269
- if (!editor) return;
32270
- const nextContent = content ?? "";
32271
- if (lastAppliedContentRef.current === nextContent) return;
32272
- lastAppliedContentRef.current = nextContent;
32273
- if (editor.getHTML() !== nextContent) {
32274
- editor.commands.setContent(nextContent, { emitUpdate: false });
32275
- }
32276
- }, [content, editor]);
32277
- (0, import_react52.useEffect)(() => {
32337
+ (0, import_react53.useEffect)(() => {
32278
32338
  if (!editor) return void 0;
32279
32339
  const proseMirror = editor.view.dom;
32280
32340
  const surface = editorContentRef.current;
@@ -32295,10 +32355,7 @@ var UEditor = import_react52.default.forwardRef(({
32295
32355
  updateActiveCellHighlight(activeTableCellRef.current);
32296
32356
  };
32297
32357
  const handleEditorMouseMove = (event) => {
32298
- const activeRowResize = rowResizeStateRef.current;
32299
- if (activeRowResize) {
32300
- setHoveredTableCell(activeRowResize.cellElement);
32301
- showRowGuide(activeRowResize.tableElement, activeRowResize.rowElement, activeRowResize.cellElement);
32358
+ if (syncActiveRowResizeGuide()) {
32302
32359
  return;
32303
32360
  }
32304
32361
  const target = resolveEventElement(event.target);
@@ -32336,7 +32393,7 @@ var UEditor = import_react52.default.forwardRef(({
32336
32393
  };
32337
32394
  const handleEditorMouseLeave = () => {
32338
32395
  clearHoveredTableCell();
32339
- if (!rowResizeStateRef.current) {
32396
+ if (!isRowResizing()) {
32340
32397
  clearAllTableResizeHover();
32341
32398
  }
32342
32399
  };
@@ -32357,75 +32414,16 @@ var UEditor = import_react52.default.forwardRef(({
32357
32414
  const row = cell.closest("tr");
32358
32415
  const table = cell.closest("table");
32359
32416
  if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) return;
32360
- if (!isRowResizeHotspot(cell, event.clientX, event.clientY)) {
32361
- return;
32362
- }
32363
- setHoveredTableCell(cell);
32364
- const rowInfo = findTableRowNodeInfo(editor.view, row);
32365
- if (!rowInfo) return;
32366
- rowResizeStateRef.current = {
32367
- rowElement: row,
32368
- tableElement: table,
32369
- cellElement: cell,
32370
- cellIndex: cell.cellIndex,
32371
- rowPos: rowInfo.pos,
32372
- rowNode: rowInfo.node,
32373
- startY: event.clientY,
32374
- startHeight: row.getBoundingClientRect().height,
32375
- previewHeight: row.getBoundingClientRect().height
32376
- };
32377
- showRowGuide(table, row, cell);
32378
- document.body.style.cursor = "row-resize";
32379
- event.preventDefault();
32380
- event.stopPropagation();
32417
+ beginResize(event, table, row, cell);
32381
32418
  };
32382
32419
  const handlePointerMove = (event) => {
32383
- const state = rowResizeStateRef.current;
32384
- if (!state) return;
32385
- const nextHeight = Math.max(
32386
- MIN_TABLE_ROW_HEIGHT,
32387
- Math.round(state.startHeight + (event.clientY - state.startY))
32388
- );
32389
- if (nextHeight === state.previewHeight) {
32390
- document.body.style.cursor = "row-resize";
32391
- showRowGuide(state.tableElement, state.rowElement, state.cellElement);
32392
- return;
32393
- }
32394
- state.previewHeight = nextHeight;
32395
- applyPreviewRowHeight(state.rowElement, nextHeight);
32396
- document.body.style.cursor = "row-resize";
32397
- showRowGuide(state.tableElement, state.rowElement, state.cellElement);
32420
+ handleRowResizePointerMove(event);
32398
32421
  };
32399
32422
  const handlePointerUp = (event) => {
32400
- const state = rowResizeStateRef.current;
32401
- if (!state) return;
32402
- const nextHeight = Math.max(
32403
- MIN_TABLE_ROW_HEIGHT,
32404
- Math.round(state.startHeight + (event.clientY - state.startY))
32405
- );
32406
- const rowNode = editor.view.state.doc.nodeAt(state.rowPos) ?? state.rowNode;
32407
- clearPreviewRowHeight(state.rowElement);
32408
- if (rowNode.attrs.rowHeight !== nextHeight) {
32409
- const tr = editor.view.state.tr;
32410
- tr.setNodeMarkup(state.rowPos, void 0, {
32411
- ...rowNode.attrs,
32412
- rowHeight: nextHeight
32413
- });
32414
- editor.view.dispatch(tr);
32415
- }
32416
- rowResizeStateRef.current = null;
32417
- document.body.style.cursor = "";
32418
- clearHoveredTableCell();
32419
- clearAllTableResizeHover();
32423
+ handleRowResizePointerUp(event);
32420
32424
  };
32421
32425
  const handleWindowBlur = () => {
32422
- const state = rowResizeStateRef.current;
32423
- if (!state) return;
32424
- clearPreviewRowHeight(state.rowElement);
32425
- rowResizeStateRef.current = null;
32426
- document.body.style.cursor = "";
32427
- clearHoveredTableCell();
32428
- clearAllTableResizeHover();
32426
+ cancelResize();
32429
32427
  };
32430
32428
  proseMirror.addEventListener("mousemove", handleEditorMouseMove);
32431
32429
  proseMirror.addEventListener("mouseleave", handleEditorMouseLeave);
@@ -32464,13 +32462,152 @@ var UEditor = import_react52.default.forwardRef(({
32464
32462
  editor.off("selectionUpdate", syncActiveTableCellFromSelection);
32465
32463
  editor.off("focus", syncActiveTableCellFromSelection);
32466
32464
  window.clearTimeout(selectionSyncTimeoutId);
32465
+ if (tableLayoutSyncFrameRef.current !== null) {
32466
+ window.cancelAnimationFrame(tableLayoutSyncFrameRef.current);
32467
+ tableLayoutSyncFrameRef.current = null;
32468
+ }
32469
+ cleanupRowResize();
32467
32470
  document.body.style.cursor = "";
32468
32471
  clearActiveTableCell();
32469
32472
  clearHoveredTableCell();
32470
32473
  clearAllTableResizeHover();
32471
- rowResizeStateRef.current = null;
32472
32474
  };
32473
- }, [clearActiveTableCell, clearAllTableResizeHover, clearHoveredTableCell, editor, hideColumnGuide, hideRowGuide, setHoveredTableCell, showColumnGuide, showRowGuide, syncActiveTableCellFromSelection, updateActiveCellHighlight]);
32475
+ }, [beginResize, cancelResize, cleanupRowResize, clearActiveTableCell, clearAllTableResizeHover, clearHoveredTableCell, editor, handleRowResizePointerMove, handleRowResizePointerUp, hideColumnGuide, hideRowGuide, isRowResizing, showColumnGuide, showRowGuide, syncActiveRowResizeGuide, syncActiveTableCellFromSelection, updateActiveCellHighlight]);
32476
+ return {
32477
+ editorContentRef,
32478
+ tableColumnGuideRef,
32479
+ tableRowGuideRef,
32480
+ activeTableCellHighlightRef
32481
+ };
32482
+ }
32483
+
32484
+ // src/components/UEditor/UEditor.tsx
32485
+ var import_jsx_runtime84 = require("react/jsx-runtime");
32486
+ var UEditor = import_react54.default.forwardRef(({
32487
+ content = "",
32488
+ onChange,
32489
+ onHtmlChange,
32490
+ onJsonChange,
32491
+ uploadImage,
32492
+ uploadImageForSave,
32493
+ uploadImageConcurrency = 3,
32494
+ imageInsertMode = "base64",
32495
+ maxImageFileSize,
32496
+ allowedImageMimeTypes,
32497
+ fallbackToDataUrl,
32498
+ placeholder,
32499
+ className,
32500
+ editable = true,
32501
+ autofocus = false,
32502
+ showToolbar = true,
32503
+ showBubbleMenu = true,
32504
+ showFloatingMenu = false,
32505
+ showCharacterCount = true,
32506
+ maxCharacters,
32507
+ minHeight = "200px",
32508
+ maxHeight = "auto",
32509
+ variant = "default",
32510
+ fontFamilies,
32511
+ fontSizes,
32512
+ lineHeights,
32513
+ letterSpacings
32514
+ }, ref) => {
32515
+ const t = useSmartTranslations("UEditor");
32516
+ const effectivePlaceholder = placeholder ?? t("placeholder");
32517
+ const inFlightPrepareRef = (0, import_react54.useRef)(null);
32518
+ const lastAppliedContentRef = (0, import_react54.useRef)(content ?? "");
32519
+ const extensions = (0, import_react54.useMemo)(
32520
+ () => buildUEditorExtensions({
32521
+ placeholder: effectivePlaceholder,
32522
+ translate: t,
32523
+ maxCharacters,
32524
+ uploadImage,
32525
+ imageInsertMode,
32526
+ maxImageFileSize,
32527
+ allowedImageMimeTypes,
32528
+ fallbackToDataUrl,
32529
+ editable
32530
+ }),
32531
+ [effectivePlaceholder, t, maxCharacters, uploadImage, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable]
32532
+ );
32533
+ const editor = (0, import_react55.useEditor)({
32534
+ immediatelyRender: false,
32535
+ extensions,
32536
+ content,
32537
+ editable,
32538
+ autofocus,
32539
+ editorProps: {
32540
+ handleDOMEvents: {
32541
+ keydown: (_view, event) => {
32542
+ if (!(event instanceof KeyboardEvent)) return false;
32543
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
32544
+ event.stopPropagation();
32545
+ }
32546
+ return false;
32547
+ },
32548
+ click: (view, event) => {
32549
+ if (!(event instanceof MouseEvent)) return false;
32550
+ if (event.button !== 0) return false;
32551
+ const target = resolveEventElement(event.target);
32552
+ const anchor = target?.closest?.("a[href]");
32553
+ const href = anchor?.getAttribute("href") ?? "";
32554
+ if (!href) return false;
32555
+ if (!view.state.selection.empty) return false;
32556
+ event.preventDefault();
32557
+ event.stopPropagation();
32558
+ window.open(href, "_blank", "noopener,noreferrer");
32559
+ return true;
32560
+ }
32561
+ },
32562
+ attributes: {
32563
+ class: UEDITOR_PROSEMIRROR_CLASS_NAME
32564
+ }
32565
+ },
32566
+ onUpdate: ({ editor: editor2 }) => {
32567
+ const html = editor2.getHTML();
32568
+ onChange?.(html);
32569
+ onHtmlChange?.(html);
32570
+ onJsonChange?.(editor2.getJSON());
32571
+ }
32572
+ });
32573
+ const {
32574
+ editorContentRef,
32575
+ tableColumnGuideRef,
32576
+ tableRowGuideRef,
32577
+ activeTableCellHighlightRef
32578
+ } = useUEditorTableInteractions(editor);
32579
+ (0, import_react54.useImperativeHandle)(
32580
+ ref,
32581
+ () => ({
32582
+ prepareContentForSave: async ({ throwOnError = false } = {}) => {
32583
+ if (!inFlightPrepareRef.current) {
32584
+ const htmlSnapshot = editor?.getHTML() ?? content ?? "";
32585
+ inFlightPrepareRef.current = prepareUEditorContentForSave({
32586
+ html: htmlSnapshot,
32587
+ uploadImageForSave,
32588
+ uploadConcurrency: uploadImageConcurrency
32589
+ }).finally(() => {
32590
+ inFlightPrepareRef.current = null;
32591
+ });
32592
+ }
32593
+ const result = await inFlightPrepareRef.current;
32594
+ if (throwOnError && result.errors.length > 0) {
32595
+ throw new UEditorPrepareContentForSaveError(result);
32596
+ }
32597
+ return result;
32598
+ }
32599
+ }),
32600
+ [content, editor, uploadImageForSave, uploadImageConcurrency]
32601
+ );
32602
+ (0, import_react54.useEffect)(() => {
32603
+ if (!editor) return;
32604
+ const nextContent = content ?? "";
32605
+ if (lastAppliedContentRef.current === nextContent) return;
32606
+ lastAppliedContentRef.current = nextContent;
32607
+ if (editor.getHTML() !== nextContent) {
32608
+ editor.commands.setContent(nextContent, { emitUpdate: false });
32609
+ }
32610
+ }, [content, editor]);
32474
32611
  if (!editor) {
32475
32612
  return /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
32476
32613
  "div",
@@ -32553,7 +32690,7 @@ var UEditor = import_react52.default.forwardRef(({
32553
32690
  ),
32554
32691
  editable && /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(TableControls, { editor, containerRef: editorContentRef }),
32555
32692
  /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
32556
- import_react53.EditorContent,
32693
+ import_react55.EditorContent,
32557
32694
  {
32558
32695
  editor,
32559
32696
  className: "min-h-full"