@underverse-ui/underverse 1.0.204 → 1.0.205

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  EmojiGridButton
3
- } from "./chunk-ZUAIBY2Z.js";
3
+ } from "./chunk-IXEFOLUD.js";
4
4
  import {
5
5
  EMOJI_LIST
6
6
  } from "./chunk-4TYW5K4J.js";
@@ -8,7 +8,7 @@ import {
8
8
  Tooltip,
9
9
  cn,
10
10
  useSmartTranslations
11
- } from "./chunk-GSBRTFP2.js";
11
+ } from "./chunk-XJR7E6QB.js";
12
12
 
13
13
  // src/components/EmojiPicker.tsx
14
14
  import { useEffect, useMemo, useRef, useState } from "react";
@@ -203,4 +203,4 @@ export {
203
203
  EmojiPicker,
204
204
  EmojiPicker_default
205
205
  };
206
- //# sourceMappingURL=chunk-CKKLFVOY.js.map
206
+ //# sourceMappingURL=chunk-Z63YNF3N.js.map
package/dist/index.cjs CHANGED
@@ -2028,16 +2028,19 @@ var init_TranslationContext = __esm({
2028
2028
  },
2029
2029
  [locale, translations]
2030
2030
  );
2031
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(GlobalI18nProvider, { i18n, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TranslationContext.Provider, { value: { locale, t }, children }) });
2031
+ const value = React6.useMemo(() => ({ locale, t }), [locale, t]);
2032
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(GlobalI18nProvider, { i18n, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TranslationContext.Provider, { value, children }) });
2032
2033
  };
2033
2034
  useUnderverseTranslations = (namespace) => {
2034
2035
  const context = React6.useContext(TranslationContext);
2035
- if (!context) {
2036
- return (key) => {
2037
- return getUnderverseDefaultTranslation("en", namespace, key);
2038
- };
2039
- }
2040
- return context.t(namespace);
2036
+ return React6.useMemo(() => {
2037
+ if (!context) {
2038
+ return (key) => {
2039
+ return getUnderverseDefaultTranslation("en", namespace, key);
2040
+ };
2041
+ }
2042
+ return context.t(namespace);
2043
+ }, [context, namespace]);
2041
2044
  };
2042
2045
  useUnderverseLocale = () => {
2043
2046
  const context = React6.useContext(TranslationContext);
@@ -2191,10 +2194,10 @@ function useSmartTranslations(namespace) {
2191
2194
  setEnvironmentLocale(detected);
2192
2195
  }
2193
2196
  }, [forceInternal, internalLocale, nextIntlBridge]);
2194
- if (forceInternal) {
2195
- return internalT;
2196
- }
2197
- return (key) => {
2197
+ return React8.useCallback((key) => {
2198
+ if (forceInternal) {
2199
+ return internalT(key);
2200
+ }
2198
2201
  const resolvedEnvironmentLocale = environmentLocale && environmentLocale !== internalLocale ? environmentLocale : null;
2199
2202
  const primaryLocale = nextIntlBridge?.locale ?? resolvedEnvironmentLocale ?? internalLocale;
2200
2203
  const fallbackLocale = resolvedEnvironmentLocale && resolvedEnvironmentLocale !== primaryLocale ? resolvedEnvironmentLocale : null;
@@ -2223,7 +2226,7 @@ function useSmartTranslations(namespace) {
2223
2226
  return internalValue;
2224
2227
  }
2225
2228
  return key;
2226
- };
2229
+ }, [environmentLocale, forceInternal, internalLocale, internalT, namespace, nextIntlBridge]);
2227
2230
  }
2228
2231
  function useSmartLocale() {
2229
2232
  const forceInternal = React8.useContext(ForceInternalContext);
@@ -5774,6 +5777,25 @@ function resolveEventElement(target) {
5774
5777
  if (target instanceof Node) return target.parentElement;
5775
5778
  return null;
5776
5779
  }
5780
+ function isPointOverRenderedText(root, clientX, clientY) {
5781
+ const view = root.ownerDocument.defaultView;
5782
+ if (!view) return false;
5783
+ const walker = root.ownerDocument.createTreeWalker(root, view.NodeFilter.SHOW_TEXT);
5784
+ let textNode = walker.nextNode();
5785
+ while (textNode) {
5786
+ if (textNode.textContent?.length) {
5787
+ const range = root.ownerDocument.createRange();
5788
+ range.selectNodeContents(textNode);
5789
+ const textRects = Array.from(range.getClientRects());
5790
+ range.detach?.();
5791
+ if (textRects.some((rect) => clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom)) {
5792
+ return true;
5793
+ }
5794
+ }
5795
+ textNode = walker.nextNode();
5796
+ }
5797
+ return false;
5798
+ }
5777
5799
  function getSelectionTableCell(view) {
5778
5800
  const browserSelection = window.getSelection();
5779
5801
  const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
@@ -44299,6 +44321,22 @@ function buildLogicalRowMetrics({
44299
44321
  };
44300
44322
  });
44301
44323
  }
44324
+ function getTableCellTextSelectionRange(editor, cellPos) {
44325
+ const cellNode = editor.state.doc.nodeAt(cellPos);
44326
+ if (!cellNode || cellNode.type.name !== "tableCell" && cellNode.type.name !== "tableHeader") {
44327
+ return null;
44328
+ }
44329
+ let from = null;
44330
+ let to = null;
44331
+ cellNode.descendants((node, relativePos) => {
44332
+ if (!node.isText || node.nodeSize === 0) return true;
44333
+ const textStart = cellPos + 1 + relativePos;
44334
+ from ?? (from = textStart);
44335
+ to = textStart + node.nodeSize;
44336
+ return true;
44337
+ });
44338
+ return from !== null && to !== null && from < to ? { from, to } : null;
44339
+ }
44302
44340
  function buildTableControlLayout(editor, surface, cell) {
44303
44341
  const row = cell.closest("tr");
44304
44342
  const table = cell.closest("table");
@@ -44404,6 +44442,9 @@ function buildTableHoverState({
44404
44442
  layout,
44405
44443
  surface
44406
44444
  }) {
44445
+ if (event.buttons !== 0) {
44446
+ return DEFAULT_TABLE_HOVER_STATE;
44447
+ }
44407
44448
  const surfaceRect = surface.getBoundingClientRect();
44408
44449
  const relativeX = event.clientX - surfaceRect.left + surface.scrollLeft;
44409
44450
  const relativeY = event.clientY - surfaceRect.top + surface.scrollTop;
@@ -44413,8 +44454,8 @@ function buildTableHoverState({
44413
44454
  const directTableMenu = targetElement?.closest?.("[data-table-control='table-menu']");
44414
44455
  const directAddColumn = targetElement?.closest?.("[data-table-control='add-column']");
44415
44456
  const directAddRow = targetElement?.closest?.("[data-table-control='add-row']");
44416
- const directRowHandleIndex = directRowHandle instanceof HTMLElement ? Number.parseInt(directRowHandle.dataset.rowHandleIndex ?? "", 10) : Number.NaN;
44417
- const directColumnHandleIndex = directColumnHandle instanceof HTMLElement ? Number.parseInt(directColumnHandle.dataset.columnHandleIndex ?? "", 10) : Number.NaN;
44457
+ const directRowHandleIndex = directRowHandle instanceof HTMLElement && relativeX <= layout.tableLeft ? Number.parseInt(directRowHandle.dataset.rowHandleIndex ?? "", 10) : Number.NaN;
44458
+ const directColumnHandleIndex = directColumnHandle instanceof HTMLElement && relativeY <= layout.tableTop ? Number.parseInt(directColumnHandle.dataset.columnHandleIndex ?? "", 10) : Number.NaN;
44418
44459
  const visibleBounds = getVisibleTableBounds(layout);
44419
44460
  const rowRailTop = layout.wrapperTop + layout.wrapperHeight;
44420
44461
  const isMouseInTable = relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + layout.tableWidth && relativeY >= layout.tableTop && relativeY <= layout.tableTop + layout.tableHeight;
@@ -44733,7 +44774,8 @@ function TableRowHandles({
44733
44774
  "data-row-handle-index": rowHandle.index,
44734
44775
  style: {
44735
44776
  top: Math.max(8, rowHandle.center - 12),
44736
- left: rowHandleLeft
44777
+ left: rowHandleLeft,
44778
+ pointerEvents: visible ? "auto" : "none"
44737
44779
  },
44738
44780
  children: /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(
44739
44781
  Tooltip,
@@ -44766,7 +44808,10 @@ function TableRowHandles({
44766
44808
  style: {
44767
44809
  opacity: isShown ? 1 : 0,
44768
44810
  transform: isShown ? "scale(1)" : `scale(${IDLE_HANDLE_SCALE})`,
44769
- pointerEvents: isShown ? "auto" : "none"
44811
+ // The idle marker is only a location hint. Keeping it
44812
+ // pointer-transparent prevents it from stealing a text
44813
+ // selection near the first character of a table cell.
44814
+ pointerEvents: visible ? "auto" : "none"
44770
44815
  },
44771
44816
  children: visible ? /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(import_lucide_react55.GripVertical, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime99.jsx)("div", { className: "h-3 w-1 rounded-full bg-muted-foreground/50 hover:bg-muted-foreground" })
44772
44817
  }
@@ -44804,7 +44849,8 @@ function TableColumnHandles({
44804
44849
  "data-column-handle-index": columnHandle.index,
44805
44850
  style: {
44806
44851
  top: columnHandleTop,
44807
- left: Math.max(8, columnHandle.center - 12)
44852
+ left: Math.max(8, columnHandle.center - 12),
44853
+ pointerEvents: visible ? "auto" : "none"
44808
44854
  },
44809
44855
  children: /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(
44810
44856
  Tooltip,
@@ -44837,7 +44883,7 @@ function TableColumnHandles({
44837
44883
  style: {
44838
44884
  opacity: isShown ? 1 : 0,
44839
44885
  transform: isShown ? "scale(1)" : `scale(${IDLE_HANDLE_SCALE})`,
44840
- pointerEvents: isShown ? "auto" : "none"
44886
+ pointerEvents: visible ? "auto" : "none"
44841
44887
  },
44842
44888
  children: visible ? /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(import_lucide_react55.GripHorizontal, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime99.jsx)("div", { className: "h-1 w-3 rounded-full bg-muted-foreground/50 hover:bg-muted-foreground" })
44843
44889
  }
@@ -45085,6 +45131,7 @@ function applyTableSize(editor, snapshot, dimensions) {
45085
45131
  // src/components/UEditor/table-controls.tsx
45086
45132
  var import_jsx_runtime101 = require("react/jsx-runtime");
45087
45133
  var TABLE_MENU_TOP_OFFSET = 10;
45134
+ var AXIS_HANDLE_RADIUS = 12;
45088
45135
  function nearestIndex(centers, position) {
45089
45136
  let bestIndex = 0;
45090
45137
  let bestDistance = Number.POSITIVE_INFINITY;
@@ -45262,6 +45309,20 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
45262
45309
  const surface2 = containerRef.current;
45263
45310
  const nextLayout = surface2 ? buildTableControlLayout(editor, surface2, cell) : null;
45264
45311
  if (!nextLayout) return;
45312
+ if (isPointOverRenderedText(cell, event.clientX, event.clientY)) {
45313
+ const textSelection = getTableCellTextSelectionRange(editor, nextLayout.cellPos);
45314
+ if (textSelection) {
45315
+ event.preventDefault();
45316
+ const didSelectText = editor.commands.setTextSelection(textSelection);
45317
+ if (didSelectText) {
45318
+ editor.view.focus();
45319
+ layoutRef.current = nextLayout;
45320
+ setLayout(nextLayout);
45321
+ setHoverState(DEFAULT_TABLE_HOVER_STATE);
45322
+ return;
45323
+ }
45324
+ }
45325
+ }
45265
45326
  const didSelect = editor.commands.setCellSelection({
45266
45327
  anchorCell: nextLayout.cellPos,
45267
45328
  headCell: nextLayout.cellPos
@@ -45728,8 +45789,8 @@ function TableControls({ editor, containerRef, showCellInspector = true }) {
45728
45789
  }
45729
45790
  const menuTop = Math.max(8, layout.tableTop - TABLE_MENU_TOP_OFFSET);
45730
45791
  const menuLeft = Math.max(8, layout.tableLeft);
45731
- const rowHandleLeft = layout.tableLeft - 12;
45732
- const columnHandleTop = layout.tableTop - 12;
45792
+ const rowHandleLeft = layout.tableLeft - AXIS_HANDLE_RADIUS;
45793
+ const columnHandleTop = layout.tableTop - AXIS_HANDLE_RADIUS;
45733
45794
  return /* @__PURE__ */ (0, import_jsx_runtime101.jsxs)(import_jsx_runtime101.Fragment, { children: [
45734
45795
  /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
45735
45796
  TableRowHandles,
@@ -46916,6 +46977,73 @@ function TableFormulaBar({ editor }) {
46916
46977
  );
46917
46978
  }
46918
46979
 
46980
+ // src/components/UEditor/use-editor-output.ts
46981
+ var React91 = __toESM(require("react"), 1);
46982
+ function normalizeDebounceMs(value) {
46983
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
46984
+ }
46985
+ function useUEditorOutput({
46986
+ onChange,
46987
+ onHtmlChange,
46988
+ onJsonChange,
46989
+ outputDebounceMs
46990
+ }) {
46991
+ const callbacksRef = React91.useRef({});
46992
+ const pendingEditorRef = React91.useRef(null);
46993
+ const timeoutRef = React91.useRef(null);
46994
+ const debounceMs = normalizeDebounceMs(outputDebounceMs);
46995
+ callbacksRef.current = { onChange, onHtmlChange, onJsonChange };
46996
+ const flushOutputUpdates = React91.useCallback(() => {
46997
+ if (timeoutRef.current !== null) {
46998
+ clearTimeout(timeoutRef.current);
46999
+ timeoutRef.current = null;
47000
+ }
47001
+ const editor = pendingEditorRef.current;
47002
+ pendingEditorRef.current = null;
47003
+ if (!editor || editor.isDestroyed) return;
47004
+ const callbacks = callbacksRef.current;
47005
+ if (callbacks.onChange || callbacks.onHtmlChange) {
47006
+ const html = editor.getHTML();
47007
+ callbacks.onChange?.(html);
47008
+ callbacks.onHtmlChange?.(html);
47009
+ }
47010
+ if (callbacks.onJsonChange) {
47011
+ callbacks.onJsonChange(editor.getJSON());
47012
+ }
47013
+ }, []);
47014
+ const scheduleOutputUpdate = React91.useCallback((editor) => {
47015
+ const callbacks = callbacksRef.current;
47016
+ if (!callbacks.onChange && !callbacks.onHtmlChange && !callbacks.onJsonChange) return;
47017
+ pendingEditorRef.current = editor;
47018
+ if (timeoutRef.current !== null) {
47019
+ clearTimeout(timeoutRef.current);
47020
+ timeoutRef.current = null;
47021
+ }
47022
+ if (debounceMs === 0) {
47023
+ flushOutputUpdates();
47024
+ return;
47025
+ }
47026
+ timeoutRef.current = setTimeout(flushOutputUpdates, debounceMs);
47027
+ }, [debounceMs, flushOutputUpdates]);
47028
+ React91.useEffect(() => {
47029
+ if (!pendingEditorRef.current || timeoutRef.current === null) return;
47030
+ clearTimeout(timeoutRef.current);
47031
+ timeoutRef.current = debounceMs === 0 ? null : setTimeout(flushOutputUpdates, debounceMs);
47032
+ if (debounceMs === 0) flushOutputUpdates();
47033
+ }, [debounceMs, flushOutputUpdates]);
47034
+ React91.useEffect(() => () => {
47035
+ if (timeoutRef.current !== null) {
47036
+ clearTimeout(timeoutRef.current);
47037
+ timeoutRef.current = null;
47038
+ }
47039
+ pendingEditorRef.current = null;
47040
+ }, []);
47041
+ return {
47042
+ flushOutputUpdates,
47043
+ scheduleOutputUpdate
47044
+ };
47045
+ }
47046
+
46919
47047
  // src/components/UEditor/UEditor.tsx
46920
47048
  var import_jsx_runtime104 = require("react/jsx-runtime");
46921
47049
  var LazyMenuBar = import_react84.default.lazy(async () => {
@@ -46927,6 +47055,7 @@ var UEditor = import_react84.default.forwardRef(({
46927
47055
  onChange,
46928
47056
  onHtmlChange,
46929
47057
  onJsonChange,
47058
+ outputDebounceMs = 0,
46930
47059
  uploadImage,
46931
47060
  uploadImageForSave,
46932
47061
  uploadImageConcurrency = 3,
@@ -46972,6 +47101,12 @@ var UEditor = import_react84.default.forwardRef(({
46972
47101
  const formulaRangePickRef = (0, import_react84.useRef)(null);
46973
47102
  const formulaRangeSurfaceRef = (0, import_react84.useRef)(null);
46974
47103
  const [formulaRangeHighlight, setFormulaRangeHighlight] = import_react84.default.useState(null);
47104
+ const { flushOutputUpdates, scheduleOutputUpdate } = useUEditorOutput({
47105
+ onChange,
47106
+ onHtmlChange,
47107
+ onJsonChange,
47108
+ outputDebounceMs
47109
+ });
46975
47110
  const scheduleFormulaRecalculate = import_react84.default.useCallback((editor2, options) => {
46976
47111
  if (editor2.isDestroyed || scheduledFormulaRecalculateRef.current) return;
46977
47112
  if (!options?.force && isEditingTableFormulaText(editor2)) return;
@@ -47113,14 +47248,7 @@ var UEditor = import_react84.default.forwardRef(({
47113
47248
  pendingFormulaTextRecalculateRef.current = false;
47114
47249
  }
47115
47250
  }
47116
- if (onChange || onHtmlChange) {
47117
- const html = editor2.getHTML();
47118
- onChange?.(html);
47119
- onHtmlChange?.(html);
47120
- }
47121
- if (onJsonChange) {
47122
- onJsonChange(editor2.getJSON());
47123
- }
47251
+ scheduleOutputUpdate(editor2);
47124
47252
  },
47125
47253
  onSelectionUpdate: ({ editor: editor2 }) => {
47126
47254
  if (pendingFormulaTextRecalculateRef.current && !isEditingTableFormulaText(editor2)) {
@@ -47134,6 +47262,7 @@ var UEditor = import_react84.default.forwardRef(({
47134
47262
  const shouldRecalculate = pendingFormulaTextRecalculateRef.current;
47135
47263
  pendingFormulaTextRecalculateRef.current = false;
47136
47264
  if (shouldRecalculate) scheduleFormulaRecalculate(editor2, { force: true });
47265
+ queueMicrotask(flushOutputUpdates);
47137
47266
  }
47138
47267
  });
47139
47268
  (0, import_react84.useEffect)(() => {