@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.js CHANGED
@@ -23109,7 +23109,7 @@ function useLocale2() {
23109
23109
  }
23110
23110
 
23111
23111
  // src/components/UEditor/UEditor.tsx
23112
- import React74, { useEffect as useEffect35, useImperativeHandle as useImperativeHandle3, useMemo as useMemo24, useRef as useRef31 } from "react";
23112
+ import React76, { useEffect as useEffect36, useImperativeHandle as useImperativeHandle3, useMemo as useMemo24, useRef as useRef33 } from "react";
23113
23113
  import { useEditor, EditorContent } from "@tiptap/react";
23114
23114
 
23115
23115
  // src/components/UEditor/extensions.ts
@@ -30715,6 +30715,111 @@ import {
30715
30715
  Table as TableIcon2,
30716
30716
  Trash2 as Trash24
30717
30717
  } from "lucide-react";
30718
+
30719
+ // src/components/UEditor/table-dom-utils.ts
30720
+ var MIN_TABLE_ROW_HEIGHT = 36;
30721
+ var COLUMN_RESIZE_LINE_THICKNESS = 2;
30722
+ var ROW_RESIZE_LINE_THICKNESS = 2;
30723
+ var UEDITOR_TABLE_LAYOUT_CHANGE_EVENT = "ueditor-table-layout-change";
30724
+ var TABLE_RESIZE_HIT_ZONE = 10;
30725
+ function findTableRowNodeInfo(view, rowElement) {
30726
+ const firstCell = rowElement.querySelector("th,td");
30727
+ if (!firstCell) return null;
30728
+ const cellPos = view.posAtDOM(firstCell, 0);
30729
+ const $pos = view.state.doc.resolve(cellPos);
30730
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
30731
+ const node = $pos.node(depth);
30732
+ if (node.type.name === "tableRow") {
30733
+ return {
30734
+ pos: $pos.before(depth),
30735
+ node
30736
+ };
30737
+ }
30738
+ }
30739
+ return null;
30740
+ }
30741
+ function resolveEventElement(target) {
30742
+ if (target instanceof Element) return target;
30743
+ if (target instanceof Node) return target.parentElement;
30744
+ return null;
30745
+ }
30746
+ function getSelectionTableCell(view) {
30747
+ const browserSelection = window.getSelection();
30748
+ const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
30749
+ const anchorCell = anchorElement?.closest?.("th,td");
30750
+ if (anchorCell instanceof HTMLElement) {
30751
+ return anchorCell;
30752
+ }
30753
+ const { from } = view.state.selection;
30754
+ const domAtPos = view.domAtPos(from);
30755
+ const element = resolveEventElement(domAtPos.node);
30756
+ const cell = element?.closest?.("th,td");
30757
+ return cell instanceof HTMLElement ? cell : null;
30758
+ }
30759
+ function isRowResizeHotspot(cell, clientX, clientY) {
30760
+ const rect = cell.getBoundingClientRect();
30761
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
30762
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
30763
+ return nearBottom && !nearRight;
30764
+ }
30765
+ function isColumnResizeHotspot(cell, clientX, clientY) {
30766
+ const rect = cell.getBoundingClientRect();
30767
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
30768
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
30769
+ return nearRight && !nearBottom;
30770
+ }
30771
+ function getRelativeBoundaryMetrics(surface, table, row, cell) {
30772
+ const surfaceRect = surface.getBoundingClientRect();
30773
+ const tableRect = table.getBoundingClientRect();
30774
+ const rowRect = row.getBoundingClientRect();
30775
+ const cellRect = cell.getBoundingClientRect();
30776
+ return {
30777
+ left: tableRect.left - surfaceRect.left + surface.scrollLeft,
30778
+ top: tableRect.top - surfaceRect.top + surface.scrollTop,
30779
+ width: tableRect.width,
30780
+ height: tableRect.height,
30781
+ rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
30782
+ columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
30783
+ };
30784
+ }
30785
+ function getRelativeCellMetrics(surface, cell) {
30786
+ const surfaceRect = surface.getBoundingClientRect();
30787
+ const cellRect = cell.getBoundingClientRect();
30788
+ return {
30789
+ left: cellRect.left - surfaceRect.left + surface.scrollLeft,
30790
+ top: cellRect.top - surfaceRect.top + surface.scrollTop,
30791
+ width: cellRect.width,
30792
+ height: cellRect.height
30793
+ };
30794
+ }
30795
+ function getRelativeSelectedCellsMetrics(surface) {
30796
+ const selectedCells = Array.from(
30797
+ surface.querySelectorAll("td.selectedCell, th.selectedCell")
30798
+ );
30799
+ if (selectedCells.length === 0) {
30800
+ return null;
30801
+ }
30802
+ const surfaceRect = surface.getBoundingClientRect();
30803
+ let left = Number.POSITIVE_INFINITY;
30804
+ let top = Number.POSITIVE_INFINITY;
30805
+ let right = Number.NEGATIVE_INFINITY;
30806
+ let bottom = Number.NEGATIVE_INFINITY;
30807
+ selectedCells.forEach((cell) => {
30808
+ const rect = cell.getBoundingClientRect();
30809
+ left = Math.min(left, rect.left);
30810
+ top = Math.min(top, rect.top);
30811
+ right = Math.max(right, rect.right);
30812
+ bottom = Math.max(bottom, rect.bottom);
30813
+ });
30814
+ return {
30815
+ left: left - surfaceRect.left + surface.scrollLeft,
30816
+ top: top - surfaceRect.top + surface.scrollTop,
30817
+ width: right - left,
30818
+ height: bottom - top
30819
+ };
30820
+ }
30821
+
30822
+ // src/components/UEditor/table-controls.tsx
30718
30823
  import { Fragment as Fragment27, jsx as jsx82, jsxs as jsxs70 } from "react/jsx-runtime";
30719
30824
  var FALLBACK_TABLE_ROW_HEIGHT = 44;
30720
30825
  var FALLBACK_TABLE_COLUMN_WIDTH = 160;
@@ -31014,6 +31119,7 @@ function TableControls({ editor, containerRef }) {
31014
31119
  surface.addEventListener("mouseover", handleSurfaceMouseMove);
31015
31120
  surface.addEventListener("mousemove", handleSurfaceMouseMove);
31016
31121
  surface.addEventListener("scroll", refreshCurrentLayout, { passive: true });
31122
+ surface.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, refreshCurrentLayout);
31017
31123
  window.addEventListener("resize", refreshCurrentLayout);
31018
31124
  editor.on("selectionUpdate", syncFromSelection);
31019
31125
  editor.on("update", refreshCurrentLayout);
@@ -31025,6 +31131,7 @@ function TableControls({ editor, containerRef }) {
31025
31131
  surface.removeEventListener("mouseover", handleSurfaceMouseMove);
31026
31132
  surface.removeEventListener("mousemove", handleSurfaceMouseMove);
31027
31133
  surface.removeEventListener("scroll", refreshCurrentLayout);
31134
+ surface.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, refreshCurrentLayout);
31028
31135
  window.removeEventListener("resize", refreshCurrentLayout);
31029
31136
  editor.off("selectionUpdate", syncFromSelection);
31030
31137
  editor.off("update", refreshCurrentLayout);
@@ -31708,194 +31815,352 @@ function TableControls({ editor, containerRef }) {
31708
31815
  ] });
31709
31816
  }
31710
31817
 
31711
- // src/components/UEditor/UEditor.tsx
31712
- import { jsx as jsx83, jsxs as jsxs71 } from "react/jsx-runtime";
31713
- var TABLE_RESIZE_HIT_ZONE = 10;
31714
- var MIN_TABLE_ROW_HEIGHT = 36;
31715
- var COLUMN_RESIZE_LINE_THICKNESS = 2;
31716
- var ROW_RESIZE_LINE_THICKNESS = 2;
31717
- function applyPreviewRowHeight(rowElement, nextHeight) {
31718
- rowElement.style.height = `${nextHeight}px`;
31719
- rowElement.style.minHeight = `${nextHeight}px`;
31720
- rowElement.querySelectorAll("th,td").forEach((cell) => {
31721
- if (cell instanceof HTMLElement) {
31722
- cell.style.height = `${nextHeight}px`;
31723
- cell.style.minHeight = `${nextHeight}px`;
31818
+ // src/components/UEditor/editor-styles.ts
31819
+ var UEDITOR_PROSEMIRROR_CLASS_NAME = cn(
31820
+ "prose prose-sm sm:prose dark:prose-invert max-w-none",
31821
+ "focus:outline-none",
31822
+ "px-4 py-4",
31823
+ "[&_.is-editor-empty]:before:content-[attr(data-placeholder)]",
31824
+ "[&_.is-editor-empty]:before:text-muted-foreground/50",
31825
+ "[&_.is-editor-empty]:before:float-left",
31826
+ "[&_.is-editor-empty]:before:pointer-events-none",
31827
+ "[&_.is-editor-empty]:before:h-0",
31828
+ "[&_ul[data-type='taskList']]:list-none",
31829
+ "[&_ul[data-type='taskList']]:pl-0",
31830
+ "[&_ul[data-type='taskList']_li]:flex",
31831
+ "[&_ul[data-type='taskList']_li]:items-start",
31832
+ "[&_ul[data-type='taskList']_li]:gap-2",
31833
+ "[&_ul[data-type='taskList']_li>label]:mt-0.5",
31834
+ "[&_ul[data-type='taskList']_li>label>input]:w-4",
31835
+ "[&_ul[data-type='taskList']_li>label>input]:h-4",
31836
+ "[&_ul[data-type='taskList']_li>label>input]:rounded",
31837
+ "[&_ul[data-type='taskList']_li>label>input]:border-2",
31838
+ "[&_ul[data-type='taskList']_li>label>input]:border-primary/50",
31839
+ "[&_ul[data-type='taskList']_li>label>input]:accent-primary",
31840
+ "[&_pre]:bg-muted/40!",
31841
+ "[&_pre]:text-foreground!",
31842
+ "[&_pre]:border!",
31843
+ "[&_pre]:border-border/60!",
31844
+ "[&_pre_code]:bg-transparent!",
31845
+ "[&_.tableWrapper]:overflow-x-auto",
31846
+ "[&_.tableWrapper]:pb-1.5",
31847
+ "[&_.tableWrapper]:select-text",
31848
+ "[&_.tableWrapper]:[scrollbar-width:thin]",
31849
+ "[&_.tableWrapper]:[scrollbar-color:hsl(var(--border))_transparent]",
31850
+ "[&_.tableWrapper::-webkit-scrollbar]:h-2",
31851
+ "[&_.tableWrapper::-webkit-scrollbar]:w-2",
31852
+ "[&_.tableWrapper::-webkit-scrollbar-track]:rounded-full",
31853
+ "[&_.tableWrapper::-webkit-scrollbar-track]:bg-transparent",
31854
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:rounded-full",
31855
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:border",
31856
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:border-solid",
31857
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:border-transparent",
31858
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:bg-border/70",
31859
+ "[&_.tableWrapper::-webkit-scrollbar-thumb:hover]:bg-muted-foreground/45",
31860
+ "[&_table]:table-fixed",
31861
+ "[&_table]:overflow-hidden",
31862
+ "[&_table]:select-text",
31863
+ "[&_table[data-table-align]]:w-max",
31864
+ "[&_table[data-table-align]]:max-w-full",
31865
+ "[&_table[data-table-align='center']]:mx-auto",
31866
+ "[&_table[data-table-align='right']]:ml-auto",
31867
+ "[&_table[data-table-align='right']]:mr-0",
31868
+ "[&_td]:relative",
31869
+ "[&_td]:align-top",
31870
+ "[&_td]:box-border",
31871
+ "[&_td]:select-text",
31872
+ "[&_th]:relative",
31873
+ "[&_th]:align-top",
31874
+ "[&_th]:box-border",
31875
+ "[&_th]:select-text",
31876
+ "[&_.selectedCell]:after:content-['']",
31877
+ "[&_.selectedCell]:after:absolute",
31878
+ "[&_.selectedCell]:after:inset-0",
31879
+ "[&_.selectedCell]:after:z-[2]",
31880
+ "[&_.selectedCell]:after:bg-primary/15",
31881
+ "[&_.selectedCell]:after:pointer-events-none",
31882
+ "[&_.column-resize-handle]:pointer-events-auto",
31883
+ "[&_.column-resize-handle]:cursor-col-resize",
31884
+ "[&_.column-resize-handle]:absolute",
31885
+ "[&_.column-resize-handle]:top-[-1px]",
31886
+ "[&_.column-resize-handle]:bottom-[-1px]",
31887
+ "[&_.column-resize-handle]:right-[-5px]",
31888
+ "[&_.column-resize-handle]:z-10",
31889
+ "[&_.column-resize-handle]:w-2.5",
31890
+ "[&_.column-resize-handle]:bg-transparent",
31891
+ "[&_.column-resize-handle]:rounded-none",
31892
+ "[&_.column-resize-handle]:opacity-0",
31893
+ "[&_.column-resize-handle]:transition-opacity",
31894
+ "[&_.column-resize-handle]:after:absolute",
31895
+ "[&_.column-resize-handle]:after:top-0",
31896
+ "[&_.column-resize-handle]:after:bottom-0",
31897
+ "[&_.column-resize-handle]:after:left-1/2",
31898
+ "[&_.column-resize-handle]:after:w-0.5",
31899
+ "[&_.column-resize-handle]:after:-translate-x-1/2",
31900
+ "[&_.column-resize-handle]:after:rounded-full",
31901
+ "[&_.column-resize-handle]:after:bg-primary/75",
31902
+ "[&_.column-resize-handle]:after:content-['']",
31903
+ "[&.resize-cursor_.column-resize-handle]:opacity-100",
31904
+ "[&.resize-cursor_.column-resize-handle]:after:bg-primary",
31905
+ "[&.resize-cursor]:cursor-col-resize",
31906
+ "[&.resize-row-cursor]:cursor-row-resize",
31907
+ "[&_img.ProseMirror-selectednode]:ring-2",
31908
+ "[&_img.ProseMirror-selectednode]:ring-primary/60",
31909
+ "[&_img.ProseMirror-selectednode]:ring-offset-2",
31910
+ "[&_img.ProseMirror-selectednode]:ring-offset-background",
31911
+ "[&_hr]:border-t-2",
31912
+ "[&_hr]:border-primary/30",
31913
+ "[&_hr]:my-8",
31914
+ "[&_h1]:text-3xl",
31915
+ "[&_h1]:font-bold",
31916
+ "[&_h1]:mt-6",
31917
+ "[&_h1]:mb-4",
31918
+ "[&_h1]:text-foreground",
31919
+ "[&_h2]:text-2xl",
31920
+ "[&_h2]:font-semibold",
31921
+ "[&_h2]:mt-5",
31922
+ "[&_h2]:mb-3",
31923
+ "[&_h2]:text-foreground",
31924
+ "[&_h3]:text-xl",
31925
+ "[&_h3]:font-semibold",
31926
+ "[&_h3]:mt-4",
31927
+ "[&_h3]:mb-2",
31928
+ "[&_h3]:text-foreground",
31929
+ "[&_ul:not([data-type='taskList'])]:list-disc",
31930
+ "[&_ul:not([data-type='taskList'])]:pl-6",
31931
+ "[&_ul:not([data-type='taskList'])]:my-3",
31932
+ "[&_ol]:list-decimal",
31933
+ "[&_ol]:pl-6",
31934
+ "[&_ol]:my-3",
31935
+ "[&_li]:my-1",
31936
+ "[&_li]:pl-1",
31937
+ "[&_li_p]:my-0",
31938
+ "[&_blockquote]:border-l-4",
31939
+ "[&_blockquote]:border-primary",
31940
+ "[&_blockquote]:pl-4",
31941
+ "[&_blockquote]:py-2",
31942
+ "[&_blockquote]:my-4",
31943
+ "[&_blockquote]:bg-muted/30",
31944
+ "[&_blockquote]:rounded-r-lg",
31945
+ "[&_blockquote]:italic",
31946
+ "[&_blockquote]:text-muted-foreground",
31947
+ "[&_blockquote_p]:my-0",
31948
+ "[&_[data-image-layout='left']+p]:mt-1",
31949
+ "[&_[data-image-layout='left']+p]:min-h-[5rem]",
31950
+ "[&_[data-image-layout='right']+p]:mt-1",
31951
+ "[&_[data-image-layout='right']+p]:min-h-[5rem]",
31952
+ "max-md:[&_[data-image-layout='left']]:float-none",
31953
+ "max-md:[&_[data-image-layout='left']]:mr-0",
31954
+ "max-md:[&_[data-image-layout='left']]:ml-0",
31955
+ "max-md:[&_[data-image-layout='left']]:max-w-full",
31956
+ "max-md:[&_[data-image-layout='right']]:float-none",
31957
+ "max-md:[&_[data-image-layout='right']]:mr-0",
31958
+ "max-md:[&_[data-image-layout='right']]:ml-0",
31959
+ "max-md:[&_[data-image-layout='right']]:max-w-full",
31960
+ "max-md:[&_[data-image-layout='left']+p]:min-h-0",
31961
+ "max-md:[&_[data-image-layout='right']+p]:min-h-0"
31962
+ );
31963
+
31964
+ // src/components/UEditor/use-table-interactions.ts
31965
+ import React75, { useEffect as useEffect35, useRef as useRef32 } from "react";
31966
+
31967
+ // src/components/UEditor/use-table-row-resize.ts
31968
+ import React74, { useRef as useRef31 } from "react";
31969
+ function useTableRowResize({
31970
+ editor,
31971
+ setHoveredTableCell,
31972
+ clearHoveredTableCell,
31973
+ showRowGuide,
31974
+ clearAllTableResizeHover,
31975
+ scheduleTableLayoutSync
31976
+ }) {
31977
+ const commitFrameRef = useRef31(null);
31978
+ const stateRef = useRef31(null);
31979
+ const commitPreview = React74.useCallback(() => {
31980
+ if (!editor) return;
31981
+ const state = stateRef.current;
31982
+ if (!state) return;
31983
+ const nextHeight = state.pendingHeight;
31984
+ if (nextHeight === state.previewHeight) {
31985
+ document.body.style.cursor = "row-resize";
31986
+ showRowGuide(state.tableElement, state.rowElement, state.cellElement);
31987
+ scheduleTableLayoutSync();
31988
+ return;
31724
31989
  }
31725
- });
31726
- }
31727
- function clearPreviewRowHeight(rowElement) {
31728
- rowElement.style.height = "";
31729
- rowElement.style.minHeight = "";
31730
- rowElement.querySelectorAll("th,td").forEach((cell) => {
31731
- if (cell instanceof HTMLElement) {
31732
- cell.style.height = "";
31733
- cell.style.minHeight = "";
31990
+ state.previewHeight = nextHeight;
31991
+ const tr = editor.view.state.tr;
31992
+ tr.setNodeMarkup(state.rowPos, void 0, {
31993
+ ...state.rowNode.attrs,
31994
+ rowHeight: nextHeight
31995
+ });
31996
+ tr.setMeta("addToHistory", false);
31997
+ editor.view.dispatch(tr);
31998
+ state.rowNode = editor.view.state.doc.nodeAt(state.rowPos) ?? state.rowNode;
31999
+ const refreshedRow = state.tableElement.rows.item(state.rowElement.rowIndex);
32000
+ if (refreshedRow instanceof HTMLTableRowElement) {
32001
+ state.rowElement = refreshedRow;
32002
+ const refreshedCell = refreshedRow.cells.item(state.cellIndex);
32003
+ if (refreshedCell instanceof HTMLTableCellElement) {
32004
+ state.cellElement = refreshedCell;
32005
+ }
32006
+ }
32007
+ document.body.style.cursor = "row-resize";
32008
+ showRowGuide(state.tableElement, state.rowElement, state.cellElement);
32009
+ scheduleTableLayoutSync();
32010
+ }, [editor, scheduleTableLayoutSync, showRowGuide]);
32011
+ const scheduleCommit = React74.useCallback(() => {
32012
+ if (commitFrameRef.current !== null) return;
32013
+ commitFrameRef.current = window.requestAnimationFrame(() => {
32014
+ commitFrameRef.current = null;
32015
+ commitPreview();
32016
+ });
32017
+ }, [commitPreview]);
32018
+ const syncActiveGuide = React74.useCallback(() => {
32019
+ const state = stateRef.current;
32020
+ if (!state) return false;
32021
+ setHoveredTableCell(state.cellElement);
32022
+ showRowGuide(state.tableElement, state.rowElement, state.cellElement);
32023
+ return true;
32024
+ }, [setHoveredTableCell, showRowGuide]);
32025
+ const isResizing = React74.useCallback(() => stateRef.current !== null, []);
32026
+ const beginResize = React74.useCallback((event, table, row, cell) => {
32027
+ if (!editor || !isRowResizeHotspot(cell, event.clientX, event.clientY)) {
32028
+ return false;
31734
32029
  }
31735
- });
31736
- }
31737
- function findTableRowNodeInfo(view, rowElement) {
31738
- const firstCell = rowElement.querySelector("th,td");
31739
- if (!firstCell) return null;
31740
- const cellPos = view.posAtDOM(firstCell, 0);
31741
- const $pos = view.state.doc.resolve(cellPos);
31742
- for (let depth = $pos.depth; depth > 0; depth -= 1) {
31743
- const node = $pos.node(depth);
31744
- if (node.type.name === "tableRow") {
31745
- return {
31746
- pos: $pos.before(depth),
31747
- node
31748
- };
32030
+ setHoveredTableCell(cell);
32031
+ const rowInfo = findTableRowNodeInfo(editor.view, row);
32032
+ if (!rowInfo) return false;
32033
+ const startHeight = row.getBoundingClientRect().height;
32034
+ stateRef.current = {
32035
+ rowElement: row,
32036
+ tableElement: table,
32037
+ cellElement: cell,
32038
+ cellIndex: cell.cellIndex,
32039
+ rowPos: rowInfo.pos,
32040
+ rowNode: rowInfo.node,
32041
+ startY: event.clientY,
32042
+ startHeight,
32043
+ previewHeight: startHeight,
32044
+ pendingHeight: startHeight
32045
+ };
32046
+ showRowGuide(table, row, cell);
32047
+ document.body.style.cursor = "row-resize";
32048
+ event.preventDefault();
32049
+ event.stopPropagation();
32050
+ return true;
32051
+ }, [editor, setHoveredTableCell, showRowGuide]);
32052
+ const handlePointerMove = React74.useCallback((event) => {
32053
+ const state = stateRef.current;
32054
+ if (!state) return;
32055
+ const nextHeight = Math.max(
32056
+ MIN_TABLE_ROW_HEIGHT,
32057
+ Math.round(state.startHeight + (event.clientY - state.startY))
32058
+ );
32059
+ if (nextHeight === state.pendingHeight) {
32060
+ document.body.style.cursor = "row-resize";
32061
+ showRowGuide(state.tableElement, state.rowElement, state.cellElement);
32062
+ return;
31749
32063
  }
31750
- }
31751
- return null;
31752
- }
31753
- function resolveEventElement(target) {
31754
- if (target instanceof Element) return target;
31755
- if (target instanceof Node) return target.parentElement;
31756
- return null;
31757
- }
31758
- function getSelectionTableCell(view) {
31759
- const browserSelection = window.getSelection();
31760
- const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
31761
- const anchorCell = anchorElement?.closest?.("th,td");
31762
- if (anchorCell instanceof HTMLElement) {
31763
- return anchorCell;
31764
- }
31765
- const { from } = view.state.selection;
31766
- const domAtPos = view.domAtPos(from);
31767
- const element = resolveEventElement(domAtPos.node);
31768
- const cell = element?.closest?.("th,td");
31769
- return cell instanceof HTMLElement ? cell : null;
31770
- }
31771
- function isRowResizeHotspot(cell, clientX, clientY) {
31772
- const rect = cell.getBoundingClientRect();
31773
- const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
31774
- const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
31775
- return nearBottom && !nearRight;
31776
- }
31777
- function isColumnResizeHotspot(cell, clientX, clientY) {
31778
- const rect = cell.getBoundingClientRect();
31779
- const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
31780
- const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
31781
- return nearRight && !nearBottom;
31782
- }
31783
- function getRelativeBoundaryMetrics(surface, table, row, cell) {
31784
- const surfaceRect = surface.getBoundingClientRect();
31785
- const tableRect = table.getBoundingClientRect();
31786
- const rowRect = row.getBoundingClientRect();
31787
- const cellRect = cell.getBoundingClientRect();
31788
- return {
31789
- left: tableRect.left - surfaceRect.left + surface.scrollLeft,
31790
- top: tableRect.top - surfaceRect.top + surface.scrollTop,
31791
- width: tableRect.width,
31792
- height: tableRect.height,
31793
- rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
31794
- columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
31795
- };
31796
- }
31797
- function getRelativeCellMetrics(surface, cell) {
31798
- const surfaceRect = surface.getBoundingClientRect();
31799
- const cellRect = cell.getBoundingClientRect();
31800
- return {
31801
- left: cellRect.left - surfaceRect.left + surface.scrollLeft,
31802
- top: cellRect.top - surfaceRect.top + surface.scrollTop,
31803
- width: cellRect.width,
31804
- height: cellRect.height
31805
- };
31806
- }
31807
- function getRelativeSelectedCellsMetrics(surface) {
31808
- const selectedCells = Array.from(
31809
- surface.querySelectorAll("td.selectedCell, th.selectedCell")
31810
- );
31811
- if (selectedCells.length === 0) {
31812
- return null;
31813
- }
31814
- const surfaceRect = surface.getBoundingClientRect();
31815
- let left = Number.POSITIVE_INFINITY;
31816
- let top = Number.POSITIVE_INFINITY;
31817
- let right = Number.NEGATIVE_INFINITY;
31818
- let bottom = Number.NEGATIVE_INFINITY;
31819
- selectedCells.forEach((cell) => {
31820
- const rect = cell.getBoundingClientRect();
31821
- left = Math.min(left, rect.left);
31822
- top = Math.min(top, rect.top);
31823
- right = Math.max(right, rect.right);
31824
- bottom = Math.max(bottom, rect.bottom);
31825
- });
32064
+ state.pendingHeight = nextHeight;
32065
+ document.body.style.cursor = "row-resize";
32066
+ scheduleCommit();
32067
+ }, [scheduleCommit, showRowGuide]);
32068
+ const handlePointerUp = React74.useCallback((event) => {
32069
+ if (!editor) return;
32070
+ const state = stateRef.current;
32071
+ if (!state) return;
32072
+ const nextHeight = Math.max(
32073
+ MIN_TABLE_ROW_HEIGHT,
32074
+ Math.round(state.startHeight + (event.clientY - state.startY))
32075
+ );
32076
+ state.pendingHeight = nextHeight;
32077
+ if (commitFrameRef.current !== null) {
32078
+ window.cancelAnimationFrame(commitFrameRef.current);
32079
+ commitFrameRef.current = null;
32080
+ }
32081
+ commitPreview();
32082
+ const latestState = stateRef.current ?? state;
32083
+ const rowNode = editor.view.state.doc.nodeAt(latestState.rowPos) ?? latestState.rowNode;
32084
+ if (rowNode.attrs.rowHeight !== nextHeight) {
32085
+ const tr = editor.view.state.tr;
32086
+ tr.setNodeMarkup(latestState.rowPos, void 0, {
32087
+ ...rowNode.attrs,
32088
+ rowHeight: nextHeight
32089
+ });
32090
+ editor.view.dispatch(tr);
32091
+ }
32092
+ stateRef.current = null;
32093
+ document.body.style.cursor = "";
32094
+ clearHoveredTableCell();
32095
+ clearAllTableResizeHover();
32096
+ scheduleTableLayoutSync();
32097
+ }, [clearAllTableResizeHover, clearHoveredTableCell, commitPreview, editor, scheduleTableLayoutSync]);
32098
+ const cancelResize = React74.useCallback(() => {
32099
+ if (!stateRef.current) return;
32100
+ if (commitFrameRef.current !== null) {
32101
+ window.cancelAnimationFrame(commitFrameRef.current);
32102
+ commitFrameRef.current = null;
32103
+ }
32104
+ stateRef.current = null;
32105
+ document.body.style.cursor = "";
32106
+ clearHoveredTableCell();
32107
+ clearAllTableResizeHover();
32108
+ scheduleTableLayoutSync();
32109
+ }, [clearAllTableResizeHover, clearHoveredTableCell, scheduleTableLayoutSync]);
32110
+ const cleanup = React74.useCallback(() => {
32111
+ if (commitFrameRef.current !== null) {
32112
+ window.cancelAnimationFrame(commitFrameRef.current);
32113
+ commitFrameRef.current = null;
32114
+ }
32115
+ stateRef.current = null;
32116
+ document.body.style.cursor = "";
32117
+ }, []);
31826
32118
  return {
31827
- left: left - surfaceRect.left + surface.scrollLeft,
31828
- top: top - surfaceRect.top + surface.scrollTop,
31829
- width: right - left,
31830
- height: bottom - top
32119
+ beginResize,
32120
+ cancelResize,
32121
+ cleanup,
32122
+ handlePointerMove,
32123
+ handlePointerUp,
32124
+ isResizing,
32125
+ syncActiveGuide
31831
32126
  };
31832
32127
  }
31833
- var UEditor = React74.forwardRef(({
31834
- content = "",
31835
- onChange,
31836
- onHtmlChange,
31837
- onJsonChange,
31838
- uploadImage,
31839
- uploadImageForSave,
31840
- uploadImageConcurrency = 3,
31841
- imageInsertMode = "base64",
31842
- maxImageFileSize,
31843
- allowedImageMimeTypes,
31844
- fallbackToDataUrl,
31845
- placeholder,
31846
- className,
31847
- editable = true,
31848
- autofocus = false,
31849
- showToolbar = true,
31850
- showBubbleMenu = true,
31851
- showFloatingMenu = false,
31852
- showCharacterCount = true,
31853
- maxCharacters,
31854
- minHeight = "200px",
31855
- maxHeight = "auto",
31856
- variant = "default",
31857
- fontFamilies,
31858
- fontSizes,
31859
- lineHeights,
31860
- letterSpacings
31861
- }, ref) => {
31862
- const t = useSmartTranslations("UEditor");
31863
- const effectivePlaceholder = placeholder ?? t("placeholder");
31864
- const inFlightPrepareRef = useRef31(null);
31865
- const lastAppliedContentRef = useRef31(content ?? "");
31866
- const editorContentRef = useRef31(null);
31867
- const tableColumnGuideRef = useRef31(null);
31868
- const tableRowGuideRef = useRef31(null);
31869
- const activeTableCellHighlightRef = useRef31(null);
31870
- const hoveredTableCellRef = useRef31(null);
31871
- const activeTableCellRef = useRef31(null);
31872
- const rowResizeStateRef = useRef31(null);
31873
- const setEditorResizeCursor = React74.useCallback((cursor) => {
32128
+
32129
+ // src/components/UEditor/use-table-interactions.ts
32130
+ function useUEditorTableInteractions(editor) {
32131
+ const editorContentRef = useRef32(null);
32132
+ const tableColumnGuideRef = useRef32(null);
32133
+ const tableRowGuideRef = useRef32(null);
32134
+ const activeTableCellHighlightRef = useRef32(null);
32135
+ const hoveredTableCellRef = useRef32(null);
32136
+ const activeTableCellRef = useRef32(null);
32137
+ const tableLayoutSyncFrameRef = useRef32(null);
32138
+ const setEditorResizeCursor = React75.useCallback((cursor) => {
31874
32139
  const proseMirror = editorContentRef.current?.querySelector(".ProseMirror");
31875
32140
  if (proseMirror) {
31876
32141
  proseMirror.style.cursor = cursor;
31877
32142
  }
31878
32143
  }, []);
31879
- const hideColumnGuide = React74.useCallback(() => {
32144
+ const hideColumnGuide = React75.useCallback(() => {
31880
32145
  editorContentRef.current?.classList.remove("resize-cursor");
31881
32146
  const guide = tableColumnGuideRef.current;
31882
32147
  if (guide) {
31883
32148
  guide.style.opacity = "0";
31884
32149
  }
31885
32150
  }, []);
31886
- const hideRowGuide = React74.useCallback(() => {
32151
+ const hideRowGuide = React75.useCallback(() => {
31887
32152
  editorContentRef.current?.classList.remove("resize-row-cursor");
31888
32153
  const guide = tableRowGuideRef.current;
31889
32154
  if (guide) {
31890
32155
  guide.style.opacity = "0";
31891
32156
  }
31892
32157
  }, []);
31893
- const clearAllTableResizeHover = React74.useCallback(() => {
32158
+ const clearAllTableResizeHover = React75.useCallback(() => {
31894
32159
  setEditorResizeCursor("");
31895
32160
  hideColumnGuide();
31896
32161
  hideRowGuide();
31897
32162
  }, [hideColumnGuide, hideRowGuide, setEditorResizeCursor]);
31898
- const updateActiveCellHighlight = React74.useCallback((cell) => {
32163
+ const updateActiveCellHighlight = React75.useCallback((cell) => {
31899
32164
  const surface = editorContentRef.current;
31900
32165
  const highlight = activeTableCellHighlightRef.current;
31901
32166
  if (!highlight) return;
@@ -31910,22 +32175,30 @@ var UEditor = React74.forwardRef(({
31910
32175
  highlight.style.width = `${metrics.width}px`;
31911
32176
  highlight.style.height = `${metrics.height}px`;
31912
32177
  }, []);
31913
- const setActiveTableCell = React74.useCallback((cell) => {
32178
+ const scheduleTableLayoutSync = React75.useCallback(() => {
32179
+ if (tableLayoutSyncFrameRef.current !== null) return;
32180
+ tableLayoutSyncFrameRef.current = window.requestAnimationFrame(() => {
32181
+ tableLayoutSyncFrameRef.current = null;
32182
+ updateActiveCellHighlight(activeTableCellRef.current);
32183
+ editorContentRef.current?.dispatchEvent(new CustomEvent(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT));
32184
+ });
32185
+ }, [updateActiveCellHighlight]);
32186
+ const setActiveTableCell = React75.useCallback((cell) => {
31914
32187
  if (activeTableCellRef.current === cell) return;
31915
32188
  activeTableCellRef.current = cell;
31916
32189
  updateActiveCellHighlight(activeTableCellRef.current);
31917
32190
  }, [updateActiveCellHighlight]);
31918
- const clearActiveTableCell = React74.useCallback(() => {
32191
+ const clearActiveTableCell = React75.useCallback(() => {
31919
32192
  activeTableCellRef.current = null;
31920
32193
  updateActiveCellHighlight(null);
31921
32194
  }, [updateActiveCellHighlight]);
31922
- const setHoveredTableCell = React74.useCallback((cell) => {
32195
+ const setHoveredTableCell = React75.useCallback((cell) => {
31923
32196
  hoveredTableCellRef.current = cell;
31924
32197
  }, []);
31925
- const clearHoveredTableCell = React74.useCallback(() => {
32198
+ const clearHoveredTableCell = React75.useCallback(() => {
31926
32199
  hoveredTableCellRef.current = null;
31927
32200
  }, []);
31928
- const showColumnGuide = React74.useCallback((table, row, cell) => {
32201
+ const showColumnGuide = React75.useCallback((table, row, cell) => {
31929
32202
  const surface = editorContentRef.current;
31930
32203
  const guide = tableColumnGuideRef.current;
31931
32204
  if (!surface || !guide) return;
@@ -31938,7 +32211,7 @@ var UEditor = React74.forwardRef(({
31938
32211
  surface.classList.add("resize-cursor");
31939
32212
  setEditorResizeCursor("col-resize");
31940
32213
  }, [setEditorResizeCursor]);
31941
- const showRowGuide = React74.useCallback((table, row, cell) => {
32214
+ const showRowGuide = React75.useCallback((table, row, cell) => {
31942
32215
  const surface = editorContentRef.current;
31943
32216
  const guide = tableRowGuideRef.current;
31944
32217
  if (!surface || !guide) return;
@@ -31951,239 +32224,26 @@ var UEditor = React74.forwardRef(({
31951
32224
  surface.classList.add("resize-row-cursor");
31952
32225
  setEditorResizeCursor("row-resize");
31953
32226
  }, [setEditorResizeCursor]);
31954
- const extensions = useMemo24(
31955
- () => buildUEditorExtensions({
31956
- placeholder: effectivePlaceholder,
31957
- translate: t,
31958
- maxCharacters,
31959
- uploadImage,
31960
- imageInsertMode,
31961
- maxImageFileSize,
31962
- allowedImageMimeTypes,
31963
- fallbackToDataUrl,
31964
- editable
31965
- }),
31966
- [effectivePlaceholder, t, maxCharacters, uploadImage, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable]
31967
- );
31968
- const editor = useEditor({
31969
- immediatelyRender: false,
31970
- extensions,
31971
- content,
31972
- editable,
31973
- autofocus,
31974
- editorProps: {
31975
- handleDOMEvents: {
31976
- keydown: (_view, event) => {
31977
- if (!(event instanceof KeyboardEvent)) return false;
31978
- if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
31979
- event.stopPropagation();
31980
- }
31981
- return false;
31982
- },
31983
- click: (view, event) => {
31984
- if (!(event instanceof MouseEvent)) return false;
31985
- if (event.button !== 0) return false;
31986
- const target = resolveEventElement(event.target);
31987
- const anchor = target?.closest?.("a[href]");
31988
- const href = anchor?.getAttribute("href") ?? "";
31989
- if (!href) return false;
31990
- if (!view.state.selection.empty) return false;
31991
- event.preventDefault();
31992
- event.stopPropagation();
31993
- window.open(href, "_blank", "noopener,noreferrer");
31994
- return true;
31995
- }
31996
- },
31997
- attributes: {
31998
- class: cn(
31999
- "prose prose-sm sm:prose dark:prose-invert max-w-none",
32000
- "focus:outline-none",
32001
- "px-4 py-4",
32002
- "[&_.is-editor-empty]:before:content-[attr(data-placeholder)]",
32003
- "[&_.is-editor-empty]:before:text-muted-foreground/50",
32004
- "[&_.is-editor-empty]:before:float-left",
32005
- "[&_.is-editor-empty]:before:pointer-events-none",
32006
- "[&_.is-editor-empty]:before:h-0",
32007
- "[&_ul[data-type='taskList']]:list-none",
32008
- "[&_ul[data-type='taskList']]:pl-0",
32009
- "[&_ul[data-type='taskList']_li]:flex",
32010
- "[&_ul[data-type='taskList']_li]:items-start",
32011
- "[&_ul[data-type='taskList']_li]:gap-2",
32012
- "[&_ul[data-type='taskList']_li>label]:mt-0.5",
32013
- "[&_ul[data-type='taskList']_li>label>input]:w-4",
32014
- "[&_ul[data-type='taskList']_li>label>input]:h-4",
32015
- "[&_ul[data-type='taskList']_li>label>input]:rounded",
32016
- "[&_ul[data-type='taskList']_li>label>input]:border-2",
32017
- "[&_ul[data-type='taskList']_li>label>input]:border-primary/50",
32018
- "[&_ul[data-type='taskList']_li>label>input]:accent-primary",
32019
- "[&_pre]:bg-muted/40!",
32020
- "[&_pre]:text-foreground!",
32021
- "[&_pre]:border!",
32022
- "[&_pre]:border-border/60!",
32023
- "[&_pre_code]:bg-transparent!",
32024
- "[&_.tableWrapper]:overflow-x-auto",
32025
- "[&_.tableWrapper]:pb-1.5",
32026
- "[&_.tableWrapper]:select-text",
32027
- "[&_.tableWrapper]:[scrollbar-width:thin]",
32028
- "[&_.tableWrapper]:[scrollbar-color:hsl(var(--border))_transparent]",
32029
- "[&_.tableWrapper::-webkit-scrollbar]:h-2",
32030
- "[&_.tableWrapper::-webkit-scrollbar]:w-2",
32031
- "[&_.tableWrapper::-webkit-scrollbar-track]:rounded-full",
32032
- "[&_.tableWrapper::-webkit-scrollbar-track]:bg-transparent",
32033
- "[&_.tableWrapper::-webkit-scrollbar-thumb]:rounded-full",
32034
- "[&_.tableWrapper::-webkit-scrollbar-thumb]:border",
32035
- "[&_.tableWrapper::-webkit-scrollbar-thumb]:border-solid",
32036
- "[&_.tableWrapper::-webkit-scrollbar-thumb]:border-transparent",
32037
- "[&_.tableWrapper::-webkit-scrollbar-thumb]:bg-border/70",
32038
- "[&_.tableWrapper::-webkit-scrollbar-thumb:hover]:bg-muted-foreground/45",
32039
- "[&_table]:table-fixed",
32040
- "[&_table]:overflow-hidden",
32041
- "[&_table]:select-text",
32042
- "[&_table[data-table-align]]:w-max",
32043
- "[&_table[data-table-align]]:max-w-full",
32044
- "[&_table[data-table-align='center']]:mx-auto",
32045
- "[&_table[data-table-align='right']]:ml-auto",
32046
- "[&_table[data-table-align='right']]:mr-0",
32047
- "[&_td]:relative",
32048
- "[&_td]:align-top",
32049
- "[&_td]:box-border",
32050
- "[&_td]:select-text",
32051
- "[&_th]:relative",
32052
- "[&_th]:align-top",
32053
- "[&_th]:box-border",
32054
- "[&_th]:select-text",
32055
- "[&_.selectedCell]:after:content-['']",
32056
- "[&_.selectedCell]:after:absolute",
32057
- "[&_.selectedCell]:after:inset-0",
32058
- "[&_.selectedCell]:after:z-[2]",
32059
- "[&_.selectedCell]:after:bg-primary/15",
32060
- "[&_.selectedCell]:after:pointer-events-none",
32061
- "[&_.column-resize-handle]:pointer-events-auto",
32062
- "[&_.column-resize-handle]:cursor-col-resize",
32063
- "[&_.column-resize-handle]:absolute",
32064
- "[&_.column-resize-handle]:top-[-1px]",
32065
- "[&_.column-resize-handle]:bottom-[-1px]",
32066
- "[&_.column-resize-handle]:right-[-5px]",
32067
- "[&_.column-resize-handle]:z-10",
32068
- "[&_.column-resize-handle]:w-2.5",
32069
- "[&_.column-resize-handle]:bg-transparent",
32070
- "[&_.column-resize-handle]:rounded-none",
32071
- "[&_.column-resize-handle]:opacity-0",
32072
- "[&_.column-resize-handle]:transition-opacity",
32073
- "[&_.column-resize-handle]:after:absolute",
32074
- "[&_.column-resize-handle]:after:top-0",
32075
- "[&_.column-resize-handle]:after:bottom-0",
32076
- "[&_.column-resize-handle]:after:left-1/2",
32077
- "[&_.column-resize-handle]:after:w-0.5",
32078
- "[&_.column-resize-handle]:after:-translate-x-1/2",
32079
- "[&_.column-resize-handle]:after:rounded-full",
32080
- "[&_.column-resize-handle]:after:bg-primary/75",
32081
- "[&_.column-resize-handle]:after:content-['']",
32082
- "[&.resize-cursor_.column-resize-handle]:opacity-100",
32083
- "[&.resize-cursor_.column-resize-handle]:after:bg-primary",
32084
- "[&.resize-cursor]:cursor-col-resize",
32085
- "[&.resize-row-cursor]:cursor-row-resize",
32086
- "[&_img.ProseMirror-selectednode]:ring-2",
32087
- "[&_img.ProseMirror-selectednode]:ring-primary/60",
32088
- "[&_img.ProseMirror-selectednode]:ring-offset-2",
32089
- "[&_img.ProseMirror-selectednode]:ring-offset-background",
32090
- "[&_hr]:border-t-2",
32091
- "[&_hr]:border-primary/30",
32092
- "[&_hr]:my-8",
32093
- "[&_h1]:text-3xl",
32094
- "[&_h1]:font-bold",
32095
- "[&_h1]:mt-6",
32096
- "[&_h1]:mb-4",
32097
- "[&_h1]:text-foreground",
32098
- "[&_h2]:text-2xl",
32099
- "[&_h2]:font-semibold",
32100
- "[&_h2]:mt-5",
32101
- "[&_h2]:mb-3",
32102
- "[&_h2]:text-foreground",
32103
- "[&_h3]:text-xl",
32104
- "[&_h3]:font-semibold",
32105
- "[&_h3]:mt-4",
32106
- "[&_h3]:mb-2",
32107
- "[&_h3]:text-foreground",
32108
- "[&_ul:not([data-type='taskList'])]:list-disc",
32109
- "[&_ul:not([data-type='taskList'])]:pl-6",
32110
- "[&_ul:not([data-type='taskList'])]:my-3",
32111
- "[&_ol]:list-decimal",
32112
- "[&_ol]:pl-6",
32113
- "[&_ol]:my-3",
32114
- "[&_li]:my-1",
32115
- "[&_li]:pl-1",
32116
- "[&_li_p]:my-0",
32117
- "[&_blockquote]:border-l-4",
32118
- "[&_blockquote]:border-primary",
32119
- "[&_blockquote]:pl-4",
32120
- "[&_blockquote]:py-2",
32121
- "[&_blockquote]:my-4",
32122
- "[&_blockquote]:bg-muted/30",
32123
- "[&_blockquote]:rounded-r-lg",
32124
- "[&_blockquote]:italic",
32125
- "[&_blockquote]:text-muted-foreground",
32126
- "[&_blockquote_p]:my-0",
32127
- "[&_[data-image-layout='left']+p]:mt-1",
32128
- "[&_[data-image-layout='left']+p]:min-h-[5rem]",
32129
- "[&_[data-image-layout='right']+p]:mt-1",
32130
- "[&_[data-image-layout='right']+p]:min-h-[5rem]",
32131
- "max-md:[&_[data-image-layout='left']]:float-none",
32132
- "max-md:[&_[data-image-layout='left']]:mr-0",
32133
- "max-md:[&_[data-image-layout='left']]:ml-0",
32134
- "max-md:[&_[data-image-layout='left']]:max-w-full",
32135
- "max-md:[&_[data-image-layout='right']]:float-none",
32136
- "max-md:[&_[data-image-layout='right']]:mr-0",
32137
- "max-md:[&_[data-image-layout='right']]:ml-0",
32138
- "max-md:[&_[data-image-layout='right']]:max-w-full",
32139
- "max-md:[&_[data-image-layout='left']+p]:min-h-0",
32140
- "max-md:[&_[data-image-layout='right']+p]:min-h-0"
32141
- )
32142
- }
32143
- },
32144
- onUpdate: ({ editor: editor2 }) => {
32145
- const html = editor2.getHTML();
32146
- onChange?.(html);
32147
- onHtmlChange?.(html);
32148
- onJsonChange?.(editor2.getJSON());
32149
- }
32227
+ const {
32228
+ beginResize,
32229
+ cancelResize,
32230
+ cleanup: cleanupRowResize,
32231
+ handlePointerMove: handleRowResizePointerMove,
32232
+ handlePointerUp: handleRowResizePointerUp,
32233
+ isResizing: isRowResizing,
32234
+ syncActiveGuide: syncActiveRowResizeGuide
32235
+ } = useTableRowResize({
32236
+ editor,
32237
+ setHoveredTableCell,
32238
+ clearHoveredTableCell,
32239
+ showRowGuide,
32240
+ clearAllTableResizeHover,
32241
+ scheduleTableLayoutSync
32150
32242
  });
32151
- const syncActiveTableCellFromSelection = React74.useCallback(() => {
32243
+ const syncActiveTableCellFromSelection = React75.useCallback(() => {
32152
32244
  if (!editor) return;
32153
32245
  setActiveTableCell(getSelectionTableCell(editor.view));
32154
32246
  }, [editor, setActiveTableCell]);
32155
- useImperativeHandle3(
32156
- ref,
32157
- () => ({
32158
- prepareContentForSave: async ({ throwOnError = false } = {}) => {
32159
- if (!inFlightPrepareRef.current) {
32160
- const htmlSnapshot = editor?.getHTML() ?? content ?? "";
32161
- inFlightPrepareRef.current = prepareUEditorContentForSave({
32162
- html: htmlSnapshot,
32163
- uploadImageForSave,
32164
- uploadConcurrency: uploadImageConcurrency
32165
- }).finally(() => {
32166
- inFlightPrepareRef.current = null;
32167
- });
32168
- }
32169
- const result = await inFlightPrepareRef.current;
32170
- if (throwOnError && result.errors.length > 0) {
32171
- throw new UEditorPrepareContentForSaveError(result);
32172
- }
32173
- return result;
32174
- }
32175
- }),
32176
- [content, editor, uploadImageForSave, uploadImageConcurrency]
32177
- );
32178
- useEffect35(() => {
32179
- if (!editor) return;
32180
- const nextContent = content ?? "";
32181
- if (lastAppliedContentRef.current === nextContent) return;
32182
- lastAppliedContentRef.current = nextContent;
32183
- if (editor.getHTML() !== nextContent) {
32184
- editor.commands.setContent(nextContent, { emitUpdate: false });
32185
- }
32186
- }, [content, editor]);
32187
32247
  useEffect35(() => {
32188
32248
  if (!editor) return void 0;
32189
32249
  const proseMirror = editor.view.dom;
@@ -32205,10 +32265,7 @@ var UEditor = React74.forwardRef(({
32205
32265
  updateActiveCellHighlight(activeTableCellRef.current);
32206
32266
  };
32207
32267
  const handleEditorMouseMove = (event) => {
32208
- const activeRowResize = rowResizeStateRef.current;
32209
- if (activeRowResize) {
32210
- setHoveredTableCell(activeRowResize.cellElement);
32211
- showRowGuide(activeRowResize.tableElement, activeRowResize.rowElement, activeRowResize.cellElement);
32268
+ if (syncActiveRowResizeGuide()) {
32212
32269
  return;
32213
32270
  }
32214
32271
  const target = resolveEventElement(event.target);
@@ -32246,7 +32303,7 @@ var UEditor = React74.forwardRef(({
32246
32303
  };
32247
32304
  const handleEditorMouseLeave = () => {
32248
32305
  clearHoveredTableCell();
32249
- if (!rowResizeStateRef.current) {
32306
+ if (!isRowResizing()) {
32250
32307
  clearAllTableResizeHover();
32251
32308
  }
32252
32309
  };
@@ -32267,75 +32324,16 @@ var UEditor = React74.forwardRef(({
32267
32324
  const row = cell.closest("tr");
32268
32325
  const table = cell.closest("table");
32269
32326
  if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) return;
32270
- if (!isRowResizeHotspot(cell, event.clientX, event.clientY)) {
32271
- return;
32272
- }
32273
- setHoveredTableCell(cell);
32274
- const rowInfo = findTableRowNodeInfo(editor.view, row);
32275
- if (!rowInfo) return;
32276
- rowResizeStateRef.current = {
32277
- rowElement: row,
32278
- tableElement: table,
32279
- cellElement: cell,
32280
- cellIndex: cell.cellIndex,
32281
- rowPos: rowInfo.pos,
32282
- rowNode: rowInfo.node,
32283
- startY: event.clientY,
32284
- startHeight: row.getBoundingClientRect().height,
32285
- previewHeight: row.getBoundingClientRect().height
32286
- };
32287
- showRowGuide(table, row, cell);
32288
- document.body.style.cursor = "row-resize";
32289
- event.preventDefault();
32290
- event.stopPropagation();
32327
+ beginResize(event, table, row, cell);
32291
32328
  };
32292
32329
  const handlePointerMove = (event) => {
32293
- const state = rowResizeStateRef.current;
32294
- if (!state) return;
32295
- const nextHeight = Math.max(
32296
- MIN_TABLE_ROW_HEIGHT,
32297
- Math.round(state.startHeight + (event.clientY - state.startY))
32298
- );
32299
- if (nextHeight === state.previewHeight) {
32300
- document.body.style.cursor = "row-resize";
32301
- showRowGuide(state.tableElement, state.rowElement, state.cellElement);
32302
- return;
32303
- }
32304
- state.previewHeight = nextHeight;
32305
- applyPreviewRowHeight(state.rowElement, nextHeight);
32306
- document.body.style.cursor = "row-resize";
32307
- showRowGuide(state.tableElement, state.rowElement, state.cellElement);
32330
+ handleRowResizePointerMove(event);
32308
32331
  };
32309
32332
  const handlePointerUp = (event) => {
32310
- const state = rowResizeStateRef.current;
32311
- if (!state) return;
32312
- const nextHeight = Math.max(
32313
- MIN_TABLE_ROW_HEIGHT,
32314
- Math.round(state.startHeight + (event.clientY - state.startY))
32315
- );
32316
- const rowNode = editor.view.state.doc.nodeAt(state.rowPos) ?? state.rowNode;
32317
- clearPreviewRowHeight(state.rowElement);
32318
- if (rowNode.attrs.rowHeight !== nextHeight) {
32319
- const tr = editor.view.state.tr;
32320
- tr.setNodeMarkup(state.rowPos, void 0, {
32321
- ...rowNode.attrs,
32322
- rowHeight: nextHeight
32323
- });
32324
- editor.view.dispatch(tr);
32325
- }
32326
- rowResizeStateRef.current = null;
32327
- document.body.style.cursor = "";
32328
- clearHoveredTableCell();
32329
- clearAllTableResizeHover();
32333
+ handleRowResizePointerUp(event);
32330
32334
  };
32331
32335
  const handleWindowBlur = () => {
32332
- const state = rowResizeStateRef.current;
32333
- if (!state) return;
32334
- clearPreviewRowHeight(state.rowElement);
32335
- rowResizeStateRef.current = null;
32336
- document.body.style.cursor = "";
32337
- clearHoveredTableCell();
32338
- clearAllTableResizeHover();
32336
+ cancelResize();
32339
32337
  };
32340
32338
  proseMirror.addEventListener("mousemove", handleEditorMouseMove);
32341
32339
  proseMirror.addEventListener("mouseleave", handleEditorMouseLeave);
@@ -32374,13 +32372,152 @@ var UEditor = React74.forwardRef(({
32374
32372
  editor.off("selectionUpdate", syncActiveTableCellFromSelection);
32375
32373
  editor.off("focus", syncActiveTableCellFromSelection);
32376
32374
  window.clearTimeout(selectionSyncTimeoutId);
32375
+ if (tableLayoutSyncFrameRef.current !== null) {
32376
+ window.cancelAnimationFrame(tableLayoutSyncFrameRef.current);
32377
+ tableLayoutSyncFrameRef.current = null;
32378
+ }
32379
+ cleanupRowResize();
32377
32380
  document.body.style.cursor = "";
32378
32381
  clearActiveTableCell();
32379
32382
  clearHoveredTableCell();
32380
32383
  clearAllTableResizeHover();
32381
- rowResizeStateRef.current = null;
32382
32384
  };
32383
- }, [clearActiveTableCell, clearAllTableResizeHover, clearHoveredTableCell, editor, hideColumnGuide, hideRowGuide, setHoveredTableCell, showColumnGuide, showRowGuide, syncActiveTableCellFromSelection, updateActiveCellHighlight]);
32385
+ }, [beginResize, cancelResize, cleanupRowResize, clearActiveTableCell, clearAllTableResizeHover, clearHoveredTableCell, editor, handleRowResizePointerMove, handleRowResizePointerUp, hideColumnGuide, hideRowGuide, isRowResizing, showColumnGuide, showRowGuide, syncActiveRowResizeGuide, syncActiveTableCellFromSelection, updateActiveCellHighlight]);
32386
+ return {
32387
+ editorContentRef,
32388
+ tableColumnGuideRef,
32389
+ tableRowGuideRef,
32390
+ activeTableCellHighlightRef
32391
+ };
32392
+ }
32393
+
32394
+ // src/components/UEditor/UEditor.tsx
32395
+ import { jsx as jsx83, jsxs as jsxs71 } from "react/jsx-runtime";
32396
+ var UEditor = React76.forwardRef(({
32397
+ content = "",
32398
+ onChange,
32399
+ onHtmlChange,
32400
+ onJsonChange,
32401
+ uploadImage,
32402
+ uploadImageForSave,
32403
+ uploadImageConcurrency = 3,
32404
+ imageInsertMode = "base64",
32405
+ maxImageFileSize,
32406
+ allowedImageMimeTypes,
32407
+ fallbackToDataUrl,
32408
+ placeholder,
32409
+ className,
32410
+ editable = true,
32411
+ autofocus = false,
32412
+ showToolbar = true,
32413
+ showBubbleMenu = true,
32414
+ showFloatingMenu = false,
32415
+ showCharacterCount = true,
32416
+ maxCharacters,
32417
+ minHeight = "200px",
32418
+ maxHeight = "auto",
32419
+ variant = "default",
32420
+ fontFamilies,
32421
+ fontSizes,
32422
+ lineHeights,
32423
+ letterSpacings
32424
+ }, ref) => {
32425
+ const t = useSmartTranslations("UEditor");
32426
+ const effectivePlaceholder = placeholder ?? t("placeholder");
32427
+ const inFlightPrepareRef = useRef33(null);
32428
+ const lastAppliedContentRef = useRef33(content ?? "");
32429
+ const extensions = useMemo24(
32430
+ () => buildUEditorExtensions({
32431
+ placeholder: effectivePlaceholder,
32432
+ translate: t,
32433
+ maxCharacters,
32434
+ uploadImage,
32435
+ imageInsertMode,
32436
+ maxImageFileSize,
32437
+ allowedImageMimeTypes,
32438
+ fallbackToDataUrl,
32439
+ editable
32440
+ }),
32441
+ [effectivePlaceholder, t, maxCharacters, uploadImage, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable]
32442
+ );
32443
+ const editor = useEditor({
32444
+ immediatelyRender: false,
32445
+ extensions,
32446
+ content,
32447
+ editable,
32448
+ autofocus,
32449
+ editorProps: {
32450
+ handleDOMEvents: {
32451
+ keydown: (_view, event) => {
32452
+ if (!(event instanceof KeyboardEvent)) return false;
32453
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
32454
+ event.stopPropagation();
32455
+ }
32456
+ return false;
32457
+ },
32458
+ click: (view, event) => {
32459
+ if (!(event instanceof MouseEvent)) return false;
32460
+ if (event.button !== 0) return false;
32461
+ const target = resolveEventElement(event.target);
32462
+ const anchor = target?.closest?.("a[href]");
32463
+ const href = anchor?.getAttribute("href") ?? "";
32464
+ if (!href) return false;
32465
+ if (!view.state.selection.empty) return false;
32466
+ event.preventDefault();
32467
+ event.stopPropagation();
32468
+ window.open(href, "_blank", "noopener,noreferrer");
32469
+ return true;
32470
+ }
32471
+ },
32472
+ attributes: {
32473
+ class: UEDITOR_PROSEMIRROR_CLASS_NAME
32474
+ }
32475
+ },
32476
+ onUpdate: ({ editor: editor2 }) => {
32477
+ const html = editor2.getHTML();
32478
+ onChange?.(html);
32479
+ onHtmlChange?.(html);
32480
+ onJsonChange?.(editor2.getJSON());
32481
+ }
32482
+ });
32483
+ const {
32484
+ editorContentRef,
32485
+ tableColumnGuideRef,
32486
+ tableRowGuideRef,
32487
+ activeTableCellHighlightRef
32488
+ } = useUEditorTableInteractions(editor);
32489
+ useImperativeHandle3(
32490
+ ref,
32491
+ () => ({
32492
+ prepareContentForSave: async ({ throwOnError = false } = {}) => {
32493
+ if (!inFlightPrepareRef.current) {
32494
+ const htmlSnapshot = editor?.getHTML() ?? content ?? "";
32495
+ inFlightPrepareRef.current = prepareUEditorContentForSave({
32496
+ html: htmlSnapshot,
32497
+ uploadImageForSave,
32498
+ uploadConcurrency: uploadImageConcurrency
32499
+ }).finally(() => {
32500
+ inFlightPrepareRef.current = null;
32501
+ });
32502
+ }
32503
+ const result = await inFlightPrepareRef.current;
32504
+ if (throwOnError && result.errors.length > 0) {
32505
+ throw new UEditorPrepareContentForSaveError(result);
32506
+ }
32507
+ return result;
32508
+ }
32509
+ }),
32510
+ [content, editor, uploadImageForSave, uploadImageConcurrency]
32511
+ );
32512
+ useEffect36(() => {
32513
+ if (!editor) return;
32514
+ const nextContent = content ?? "";
32515
+ if (lastAppliedContentRef.current === nextContent) return;
32516
+ lastAppliedContentRef.current = nextContent;
32517
+ if (editor.getHTML() !== nextContent) {
32518
+ editor.commands.setContent(nextContent, { emitUpdate: false });
32519
+ }
32520
+ }, [content, editor]);
32384
32521
  if (!editor) {
32385
32522
  return /* @__PURE__ */ jsx83(
32386
32523
  "div",