@revisium/schema-toolkit-ui 0.6.0 → 0.6.1

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
@@ -7033,6 +7033,9 @@ function selectDefaultColumns(columns, maxVisible = 4) {
7033
7033
  //#region src/table-editor/Columns/model/ColumnsModel.ts
7034
7034
  const DEFAULT_COLUMN_WIDTH = 150;
7035
7035
  const DEFAULT_ID_COLUMN_WIDTH = 240;
7036
+ function fieldToCssVar(field) {
7037
+ return `--cw-${field.replaceAll(".", "-")}`;
7038
+ }
7036
7039
  function isValidPinZoneOrder(fields, pins) {
7037
7040
  let zone = "left";
7038
7041
  for (const field of fields) {
@@ -7222,7 +7225,7 @@ var ColumnsModel = class {
7222
7225
  setColumnWidth(field, width) {
7223
7226
  this._isResizing = true;
7224
7227
  this._columnWidths.set(field, width);
7225
- if (this._wrapperElement) this._wrapperElement.style.setProperty(`--cw-${field}`, `${width}px`);
7228
+ if (this._wrapperElement) this._wrapperElement.style.setProperty(fieldToCssVar(field), `${width}px`);
7226
7229
  }
7227
7230
  commitColumnWidth() {
7228
7231
  this._isResizing = false;
@@ -7242,7 +7245,7 @@ var ColumnsModel = class {
7242
7245
  return this._buildCssVarsUntracked();
7243
7246
  }
7244
7247
  columnWidthCssVar(field) {
7245
- return `var(--cw-${field}, ${field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH}px)`;
7248
+ return `var(${fieldToCssVar(field)}, ${field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH}px)`;
7246
7249
  }
7247
7250
  getPinState(field) {
7248
7251
  return this._pinnedColumns.get(field);
@@ -7469,7 +7472,7 @@ var ColumnsModel = class {
7469
7472
  const vars = {};
7470
7473
  for (const field of this._visibleFields) {
7471
7474
  const width = this._columnWidths.get(field) ?? (field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH);
7472
- vars[`--cw-${field}`] = `${width}px`;
7475
+ vars[fieldToCssVar(field)] = `${width}px`;
7473
7476
  }
7474
7477
  return vars;
7475
7478
  }
@@ -7478,7 +7481,7 @@ var ColumnsModel = class {
7478
7481
  const vars = {};
7479
7482
  for (const field of this._visibleFields) {
7480
7483
  const width = this._columnWidths.get(field) ?? (field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH);
7481
- vars[`--cw-${field}`] = `${width}px`;
7484
+ vars[fieldToCssVar(field)] = `${width}px`;
7482
7485
  }
7483
7486
  return vars;
7484
7487
  });
@@ -10851,6 +10854,242 @@ const CellContextMenu = (0, mobx_react_lite.observer)(({ cell, onEditPointerDown
10851
10854
  }) }) });
10852
10855
  });
10853
10856
 
10857
+ //#endregion
10858
+ //#region src/table-editor/Table/ui/Cell/cellStyles.ts
10859
+ const SELECTION_BORDER_COLOR = "#3b82f6";
10860
+ function getCellState(cell) {
10861
+ if (cell.isReadOnly) {
10862
+ if (cell.isFocused && !cell.isInSelection) return "readonlyFocused";
10863
+ if (cell.isInSelection) return "selected";
10864
+ return "readonly";
10865
+ }
10866
+ if (cell.isEditing) return "editing";
10867
+ if (cell.isFocused && !cell.isInSelection) return "focused";
10868
+ if (cell.isInSelection) return "selected";
10869
+ return "display";
10870
+ }
10871
+ function isPrintableKey(e) {
10872
+ if (e.ctrlKey || e.metaKey || e.altKey) return false;
10873
+ return e.key.length === 1;
10874
+ }
10875
+
10876
+ //#endregion
10877
+ //#region src/table-editor/Table/ui/Cell/cellCss.ts
10878
+ const CLS = "cw";
10879
+ const CLS_DISPLAY = `${CLS} cw-display`;
10880
+ const CLS_READONLY = `${CLS} cw-readonly`;
10881
+ const CLS_FOCUSED = `${CLS} cw-focused`;
10882
+ const CLS_EDITING = `${CLS} cw-editing`;
10883
+ const CLS_READONLY_FOCUSED = `${CLS} cw-readonlyFocused`;
10884
+ const CLS_SELECTED = `${CLS} cw-selected`;
10885
+ const CLS_ANCHOR = "cw-anchor";
10886
+ const STATE_CLASS = {
10887
+ display: CLS_DISPLAY,
10888
+ readonly: CLS_READONLY,
10889
+ focused: CLS_FOCUSED,
10890
+ editing: CLS_EDITING,
10891
+ readonlyFocused: CLS_READONLY_FOCUSED,
10892
+ selected: CLS_SELECTED
10893
+ };
10894
+ const CELL_STYLE_ID = "cell-wrapper-styles";
10895
+ function ensureCellStyles() {
10896
+ if (typeof document === "undefined") return;
10897
+ if (document.getElementById(CELL_STYLE_ID)) return;
10898
+ const style = document.createElement("style");
10899
+ style.id = CELL_STYLE_ID;
10900
+ style.textContent = [
10901
+ ".cw{height:40px;padding:0 8px;position:relative;overflow:hidden;cursor:cell;box-shadow:var(--cw-shadow,none)}",
10902
+ ".cw:focus,.cw:focus-visible{outline:none;box-shadow:var(--cw-shadow,none)}",
10903
+ ".cw-display:hover,.cw-readonly:hover{background-color:var(--chakra-colors-gray-50);box-shadow:inset 0 -1px 0 0 #ededed}",
10904
+ ".cw-focused{background-color:var(--chakra-colors-blue-50)}",
10905
+ ".cw-focused::before,.cw-editing::before,.cw-readonlyFocused::before,.cw-anchor::before{content:\"\";position:absolute;inset:1px;border-radius:1px;pointer-events:none}",
10906
+ ".cw-focused::before{border:2px solid var(--chakra-colors-blue-400)}",
10907
+ ".cw-editing{cursor:text;background-color:white;z-index:1}",
10908
+ ".cw-editing::before{border:2px solid var(--chakra-colors-blue-500)}",
10909
+ ".cw-readonlyFocused{background-color:var(--chakra-colors-gray-50)}",
10910
+ ".cw-readonlyFocused::before{border:2px solid var(--chakra-colors-gray-400)}",
10911
+ ".cw-selected{background-color:var(--chakra-colors-blue-100);user-select:none}",
10912
+ ".cw-anchor::before{border:2px solid var(--chakra-colors-blue-400)}"
10913
+ ].join("");
10914
+ document.head.appendChild(style);
10915
+ }
10916
+ const INNER_STYLE = {
10917
+ display: "flex",
10918
+ alignItems: "center",
10919
+ height: "100%",
10920
+ width: "100%",
10921
+ minWidth: 0,
10922
+ overflow: "hidden"
10923
+ };
10924
+ function buildSelectionBoxShadow(edges) {
10925
+ const shadows = [];
10926
+ if (edges.top) shadows.push(`inset 0 2px 0 0 ${SELECTION_BORDER_COLOR}`);
10927
+ if (edges.bottom) shadows.push(`inset 0 -2px 0 0 ${SELECTION_BORDER_COLOR}`);
10928
+ if (edges.left) shadows.push(`inset 2px 0 0 0 ${SELECTION_BORDER_COLOR}`);
10929
+ if (edges.right) shadows.push(`inset -2px 0 0 0 ${SELECTION_BORDER_COLOR}`);
10930
+ return shadows.length > 0 ? shadows.join(", ") : null;
10931
+ }
10932
+
10933
+ //#endregion
10934
+ //#region src/table-editor/Table/ui/Cell/useCellContextMenu.ts
10935
+ let pendingContextMenu = null;
10936
+ function clearPendingContextMenu() {
10937
+ const pending = pendingContextMenu;
10938
+ pendingContextMenu = null;
10939
+ return pending;
10940
+ }
10941
+ function setPendingContextMenu(value) {
10942
+ pendingContextMenu = value;
10943
+ }
10944
+ const cellMenuRegistry = /* @__PURE__ */ new WeakMap();
10945
+ function useCellContextMenu(cell, cellRef, deferredEdit) {
10946
+ const [menuAnchor, setMenuAnchor] = (0, react.useState)(null);
10947
+ const menuOpen = menuAnchor !== null;
10948
+ const openContextMenuAt = (0, react.useCallback)((clientX, clientY) => {
10949
+ if (!cell.isFocused && !cell.isInSelection) cell.focus();
10950
+ setMenuAnchor(new DOMRect(clientX, clientY, 0, 0));
10951
+ }, [cell]);
10952
+ (0, react.useEffect)(() => {
10953
+ const el = cellRef.current;
10954
+ if (el) cellMenuRegistry.set(el, openContextMenuAt);
10955
+ return () => {
10956
+ if (el) cellMenuRegistry.delete(el);
10957
+ };
10958
+ }, [cellRef, openContextMenuAt]);
10959
+ const menuCloseRef = (0, react.useRef)(null);
10960
+ const handleMenuClose = (0, react.useCallback)(() => {
10961
+ const pending = clearPendingContextMenu();
10962
+ const didTriggerEdit = deferredEdit.triggerIfRequested();
10963
+ if (!pending && !didTriggerEdit && !cell.isEditing) if (cell.isAnchor || cell.isFocused && !cell.hasRangeSelection) cellRef.current?.focus();
10964
+ else ((cellRef.current?.closest("[data-testid=\"table-widget\"]"))?.querySelector("[data-testid^=\"cell-\"][tabindex=\"0\"]"))?.focus();
10965
+ setMenuAnchor(null);
10966
+ if (pending) {
10967
+ const openFn = cellMenuRegistry.get(pending.target);
10968
+ if (openFn) setTimeout(() => {
10969
+ openFn(pending.clientX, pending.clientY);
10970
+ }, 0);
10971
+ }
10972
+ }, [
10973
+ cell,
10974
+ cellRef,
10975
+ deferredEdit
10976
+ ]);
10977
+ menuCloseRef.current = handleMenuClose;
10978
+ return {
10979
+ menuAnchor,
10980
+ menuOpen,
10981
+ openContextMenuAt,
10982
+ handleMenuClose,
10983
+ menuCloseRef
10984
+ };
10985
+ }
10986
+
10987
+ //#endregion
10988
+ //#region src/table-editor/Table/ui/Cell/useCellFocus.ts
10989
+ function useCellFocus(cellRef, state, isAnchorInRange, navVersion) {
10990
+ (0, react.useEffect)(() => {
10991
+ if (!cellRef.current) return;
10992
+ if (state === "focused" || state === "readonlyFocused" || isAnchorInRange) cellRef.current.focus();
10993
+ else if (state === "display" || state === "readonly" || state === "selected") cellRef.current.blur();
10994
+ }, [
10995
+ cellRef,
10996
+ state,
10997
+ isAnchorInRange,
10998
+ navVersion
10999
+ ]);
11000
+ }
11001
+
11002
+ //#endregion
11003
+ //#region src/table-editor/Table/ui/Cell/useCellKeyboard.ts
11004
+ function handleArrowKey(cell, e, shiftAction, moveAction) {
11005
+ e.preventDefault();
11006
+ if (e.shiftKey) shiftAction();
11007
+ else if (cell.hasRangeSelection) cell.focus();
11008
+ else moveAction();
11009
+ }
11010
+ function handleEditableKeys(cell, e, callbacks) {
11011
+ if (cell.isReadOnly) {
11012
+ if (e.key === "Enter" || e.key === "Delete" || e.key === "Backspace" || isPrintableKey(e)) {
11013
+ e.preventDefault();
11014
+ cell.notifyReadonlyEditAttempt();
11015
+ }
11016
+ return;
11017
+ }
11018
+ const hasRange = cell.hasRangeSelection;
11019
+ if (!hasRange && e.key === "Enter") {
11020
+ e.preventDefault();
11021
+ if (callbacks.onStartEdit) callbacks.onStartEdit();
11022
+ else if (callbacks.onDoubleClick) callbacks.onDoubleClick();
11023
+ } else if (!hasRange && (e.key === "Delete" || e.key === "Backspace") && callbacks.onDelete) {
11024
+ e.preventDefault();
11025
+ callbacks.onDelete();
11026
+ } else if (isPrintableKey(e) && callbacks.onTypeChar) {
11027
+ e.preventDefault();
11028
+ callbacks.onTypeChar(e.key);
11029
+ }
11030
+ }
11031
+ function useCellKeyboard(cell, state, isAnchorInRange, menuOpen, menuCloseRef, callbacks) {
11032
+ const { onStartEdit, onDoubleClick, onTypeChar, onDelete } = callbacks;
11033
+ return {
11034
+ handleKeyDown: (0, react.useCallback)((e) => {
11035
+ if (menuOpen && e.key === "Escape") {
11036
+ e.preventDefault();
11037
+ menuCloseRef.current?.();
11038
+ return;
11039
+ }
11040
+ if (!(state === "focused" || state === "readonlyFocused" || isAnchorInRange)) return;
11041
+ const isMod = e.ctrlKey || e.metaKey;
11042
+ if (isMod && e.key === "c" && !cell.hasRangeSelection) {
11043
+ e.preventDefault();
11044
+ cell.copyToClipboard();
11045
+ return;
11046
+ }
11047
+ if (isMod && e.key === "v") return;
11048
+ const arrow = {
11049
+ ArrowUp: [cell.shiftMoveUp, cell.moveUp],
11050
+ ArrowDown: [cell.shiftMoveDown, cell.moveDown],
11051
+ ArrowLeft: [cell.shiftMoveLeft, cell.moveLeft],
11052
+ ArrowRight: [cell.shiftMoveRight, cell.moveRight]
11053
+ }[e.key];
11054
+ if (e.key === "Escape") {
11055
+ e.preventDefault();
11056
+ if (cell.hasRangeSelection) cell.focus();
11057
+ else cell.blur();
11058
+ } else if (arrow) handleArrowKey(cell, e, arrow[0], arrow[1]);
11059
+ else if (e.key === "Tab") {
11060
+ e.preventDefault();
11061
+ cell.handleTab(e.shiftKey);
11062
+ } else handleEditableKeys(cell, e, {
11063
+ onStartEdit,
11064
+ onDoubleClick,
11065
+ onTypeChar,
11066
+ onDelete
11067
+ });
11068
+ }, [
11069
+ state,
11070
+ cell,
11071
+ isAnchorInRange,
11072
+ menuOpen,
11073
+ menuCloseRef,
11074
+ onStartEdit,
11075
+ onDoubleClick,
11076
+ onTypeChar,
11077
+ onDelete
11078
+ ]),
11079
+ handleDoubleClick: (0, react.useCallback)((e) => {
11080
+ if (state === "readonly" || state === "readonlyFocused") {
11081
+ cell.notifyReadonlyEditAttempt();
11082
+ return;
11083
+ }
11084
+ onDoubleClick?.(e.clientX);
11085
+ }, [
11086
+ state,
11087
+ cell,
11088
+ onDoubleClick
11089
+ ])
11090
+ };
11091
+ }
11092
+
10854
11093
  //#endregion
10855
11094
  //#region src/table-editor/Table/ui/Cell/useDeferredMenuEdit.ts
10856
11095
  /**
@@ -10905,299 +11144,117 @@ function useDeferredMenuEdit(editFn) {
10905
11144
  };
10906
11145
  }
10907
11146
 
10908
- //#endregion
10909
- //#region src/table-editor/Table/ui/borderConstants.ts
10910
- const CELL_BORDER_COLOR = "#ededed";
10911
- const BOTTOM_BORDER_SHADOW = `inset 0 -1px 0 0 ${CELL_BORDER_COLOR}`;
10912
- function buildAddColumnShadowCss() {
10913
- return { "&::after": {
10914
- content: "\"\"",
10915
- position: "absolute",
10916
- top: 0,
10917
- bottom: 0,
10918
- width: "8px",
10919
- left: "-8px",
10920
- pointerEvents: "none",
10921
- transition: "opacity 0.15s",
10922
- opacity: "var(--shadow-right-opacity, 0)",
10923
- boxShadow: "inset -8px 0 12px -8px rgba(0,0,0,0.1)"
10924
- } };
10925
- }
10926
- function adjustRightOffsetCss(rightCss, addColOffset) {
10927
- if (addColOffset <= 0) return rightCss;
10928
- if (rightCss === "0px") return `${addColOffset}px`;
10929
- return `calc(${rightCss} + ${addColOffset}px)`;
10930
- }
10931
-
10932
- //#endregion
10933
- //#region src/table-editor/Table/ui/Cell/cellStyles.ts
10934
- const FOCUS_RING_RESET = {
10935
- outline: "none",
10936
- boxShadow: "none"
10937
- };
10938
- const SELECTION_BORDER_COLOR = "#3b82f6";
10939
- function buildSelectionBoxShadow(edges) {
10940
- const shadows = [];
10941
- if (edges.top) shadows.push(`inset 0 2px 0 0 ${SELECTION_BORDER_COLOR}`);
10942
- if (edges.bottom) shadows.push(`inset 0 -2px 0 0 ${SELECTION_BORDER_COLOR}`);
10943
- if (edges.left) shadows.push(`inset 2px 0 0 0 ${SELECTION_BORDER_COLOR}`);
10944
- if (edges.right) shadows.push(`inset -2px 0 0 0 ${SELECTION_BORDER_COLOR}`);
10945
- return shadows.length > 0 ? shadows.join(", ") : null;
10946
- }
10947
- const stateStyles = {
10948
- display: {
10949
- cursor: "cell",
10950
- _hover: {
10951
- bg: "gray.50",
10952
- boxShadow: BOTTOM_BORDER_SHADOW
10953
- }
10954
- },
10955
- focused: {
10956
- cursor: "cell",
10957
- bg: "blue.50",
10958
- _before: {
10959
- content: "\"\"",
10960
- position: "absolute",
10961
- inset: "1px",
10962
- border: "2px solid",
10963
- borderColor: "blue.400",
10964
- borderRadius: "1px",
10965
- pointerEvents: "none"
10966
- },
10967
- _focus: FOCUS_RING_RESET,
10968
- _focusVisible: FOCUS_RING_RESET
10969
- },
10970
- editing: {
10971
- cursor: "text",
10972
- bg: "white",
10973
- zIndex: 1,
10974
- _before: {
10975
- content: "\"\"",
10976
- position: "absolute",
10977
- inset: "1px",
10978
- border: "2px solid",
10979
- borderColor: "blue.500",
10980
- borderRadius: "1px",
10981
- pointerEvents: "none"
10982
- },
10983
- _focus: FOCUS_RING_RESET,
10984
- _focusVisible: FOCUS_RING_RESET
10985
- },
10986
- readonly: {
10987
- cursor: "cell",
10988
- _hover: {
10989
- bg: "gray.50",
10990
- boxShadow: BOTTOM_BORDER_SHADOW
10991
- }
10992
- },
10993
- readonlyFocused: {
10994
- cursor: "cell",
10995
- bg: "gray.50",
10996
- _before: {
10997
- content: "\"\"",
10998
- position: "absolute",
10999
- inset: "1px",
11000
- border: "2px solid",
11001
- borderColor: "gray.400",
11002
- borderRadius: "1px",
11003
- pointerEvents: "none"
11004
- },
11005
- _focus: FOCUS_RING_RESET,
11006
- _focusVisible: FOCUS_RING_RESET
11007
- },
11008
- selected: {
11009
- cursor: "cell",
11010
- bg: "blue.100"
11011
- }
11012
- };
11013
- function getCellState(cell) {
11014
- if (cell.isReadOnly) {
11015
- if (cell.isFocused && !cell.isInSelection) return "readonlyFocused";
11016
- if (cell.isInSelection) return "selected";
11017
- return "readonly";
11018
- }
11019
- if (cell.isEditing) return "editing";
11020
- if (cell.isFocused && !cell.isInSelection) return "focused";
11021
- if (cell.isInSelection) return "selected";
11022
- return "display";
11023
- }
11024
- function isPrintableKey(e) {
11025
- if (e.ctrlKey || e.metaKey || e.altKey) return false;
11026
- return e.key.length === 1;
11027
- }
11028
-
11029
11147
  //#endregion
11030
11148
  //#region src/table-editor/Table/ui/Cell/CellWrapper.tsx
11031
- function handleArrowKey(cell, e, shiftAction, moveAction) {
11032
- e.preventDefault();
11033
- if (e.shiftKey) shiftAction();
11034
- else if (cell.hasRangeSelection) cell.focus();
11035
- else moveAction();
11036
- }
11037
- function handleEditableKeys(cell, e, onStartEdit, onDoubleClick, onTypeChar, onDelete) {
11038
- if (cell.isReadOnly) {
11039
- if (e.key === "Enter" || e.key === "Delete" || e.key === "Backspace" || isPrintableKey(e)) cell.notifyReadonlyEditAttempt();
11040
- return;
11041
- }
11042
- const hasRange = cell.hasRangeSelection;
11043
- if (!hasRange && e.key === "Enter") {
11044
- e.preventDefault();
11045
- if (onStartEdit) onStartEdit();
11046
- else if (onDoubleClick) onDoubleClick();
11047
- } else if (!hasRange && (e.key === "Delete" || e.key === "Backspace") && onDelete) {
11048
- e.preventDefault();
11049
- onDelete();
11050
- } else if (isPrintableKey(e) && onTypeChar) {
11051
- e.preventDefault();
11052
- onTypeChar(e.key);
11053
- }
11054
- }
11149
+ const LazyContextMenu = (0, mobx_react_lite.observer)(({ cell, cellRef, anchorRect, onClose, onEditPointerDown }) => {
11150
+ const handleOpenChange = (0, react.useCallback)((details) => {
11151
+ if (!details.open) onClose();
11152
+ }, [onClose]);
11153
+ const handleInteractOutside = (0, react.useCallback)((e) => {
11154
+ const originalEvent = e.detail?.originalEvent;
11155
+ if (!originalEvent || originalEvent.button !== 2) return;
11156
+ const targetCell = originalEvent.target?.closest("[data-testid^=\"cell-\"]");
11157
+ if (targetCell) setPendingContextMenu({
11158
+ target: targetCell,
11159
+ clientX: originalEvent.clientX,
11160
+ clientY: originalEvent.clientY
11161
+ });
11162
+ }, []);
11163
+ const getAnchorRect = (0, react.useCallback)(() => {
11164
+ return anchorRect ?? (cellRef.current?.getBoundingClientRect() || null);
11165
+ }, [anchorRect, cellRef]);
11166
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Menu.Root, {
11167
+ open: true,
11168
+ onOpenChange: handleOpenChange,
11169
+ onInteractOutside: handleInteractOutside,
11170
+ positioning: {
11171
+ placement: "bottom-start",
11172
+ getAnchorRect
11173
+ },
11174
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CellContextMenu, {
11175
+ cell,
11176
+ onEditPointerDown
11177
+ })
11178
+ });
11179
+ });
11055
11180
  const CellWrapper = (0, mobx_react_lite.observer)(({ cell, children, onDoubleClick, onStartEdit, onTypeChar, onDelete }) => {
11181
+ ensureCellStyles();
11056
11182
  const cellRef = (0, react.useRef)(null);
11057
- const menuOpenRef = (0, react.useRef)(false);
11058
- const deferredEdit = useDeferredMenuEdit(onStartEdit ?? onDoubleClick);
11059
11183
  const state = getCellState(cell);
11060
11184
  const selectionEdges = cell.selectionEdges;
11061
11185
  const isAnchorInRange = cell.isAnchor && cell.isInSelection;
11062
11186
  const navVersion = cell.navigationVersion;
11063
- (0, react.useEffect)(() => {
11064
- if (!cellRef.current) return;
11065
- if (state === "focused" || state === "readonlyFocused" || isAnchorInRange) cellRef.current.focus();
11066
- else if (state === "display" || state === "readonly" || state === "selected") cellRef.current.blur();
11067
- }, [
11068
- state,
11069
- isAnchorInRange,
11070
- navVersion
11071
- ]);
11072
- const handleClick = (0, react.useCallback)((e) => {
11073
- if (state === "editing") return;
11074
- if (e.shiftKey) cell.selectTo();
11075
- else cell.focus();
11076
- }, [state, cell]);
11187
+ const isActive = state === "focused" || state === "editing" || state === "readonlyFocused" || isAnchorInRange;
11188
+ const deferredEdit = useDeferredMenuEdit(onStartEdit ?? onDoubleClick);
11189
+ const { menuAnchor, menuOpen, openContextMenuAt, handleMenuClose, menuCloseRef } = useCellContextMenu(cell, cellRef, deferredEdit);
11190
+ useCellFocus(cellRef, state, isAnchorInRange, navVersion);
11191
+ const { handleKeyDown, handleDoubleClick } = useCellKeyboard(cell, state, isAnchorInRange, menuOpen, menuCloseRef, {
11192
+ onStartEdit,
11193
+ onDoubleClick,
11194
+ onTypeChar,
11195
+ onDelete
11196
+ });
11077
11197
  const handleMouseDown = (0, react.useCallback)((e) => {
11078
11198
  if (e.detail === 2 && state !== "readonly" && state !== "readonlyFocused") e.preventDefault();
11199
+ if (e.button === 2) {
11200
+ openContextMenuAt(e.clientX, e.clientY);
11201
+ return;
11202
+ }
11079
11203
  if (!e.shiftKey && e.button === 0 && state !== "editing") {
11080
11204
  e.preventDefault();
11081
11205
  cell.dragStart();
11082
11206
  cellRef.current?.focus();
11083
11207
  }
11084
- }, [state, cell]);
11085
- const handleMouseEnter = (0, react.useCallback)((e) => {
11086
- if (e.buttons === 1) cell.dragExtend();
11087
- }, [cell]);
11088
- const handleDoubleClick = (0, react.useCallback)((e) => {
11089
- if (state === "readonly" || state === "readonlyFocused") {
11090
- cell.notifyReadonlyEditAttempt();
11091
- return;
11092
- }
11093
- onDoubleClick?.(e.clientX);
11094
11208
  }, [
11095
11209
  state,
11096
11210
  cell,
11097
- onDoubleClick
11211
+ openContextMenuAt
11098
11212
  ]);
11099
- const handleKeyDown = (0, react.useCallback)((e) => {
11100
- if (!(state === "focused" || state === "readonlyFocused" || isAnchorInRange)) return;
11101
- const isMod = e.ctrlKey || e.metaKey;
11102
- if (isMod && e.key === "c" && !cell.hasRangeSelection) {
11103
- e.preventDefault();
11104
- cell.copyToClipboard();
11105
- return;
11106
- }
11107
- if (isMod && e.key === "v") return;
11108
- if (e.key === "Escape") {
11109
- e.preventDefault();
11110
- if (cell.hasRangeSelection) cell.focus();
11111
- else cell.blur();
11112
- } else if (e.key === "ArrowUp") handleArrowKey(cell, e, cell.shiftMoveUp, cell.moveUp);
11113
- else if (e.key === "ArrowDown") handleArrowKey(cell, e, cell.shiftMoveDown, cell.moveDown);
11114
- else if (e.key === "ArrowLeft") handleArrowKey(cell, e, cell.shiftMoveLeft, cell.moveLeft);
11115
- else if (e.key === "ArrowRight") handleArrowKey(cell, e, cell.shiftMoveRight, cell.moveRight);
11116
- else if (e.key === "Tab") {
11117
- e.preventDefault();
11118
- cell.handleTab(e.shiftKey);
11119
- } else handleEditableKeys(cell, e, onStartEdit, onDoubleClick, onTypeChar, onDelete);
11120
- }, [
11121
- state,
11122
- cell,
11123
- isAnchorInRange,
11124
- onStartEdit,
11125
- onDoubleClick,
11126
- onTypeChar,
11127
- onDelete
11128
- ]);
11129
- const selectionShadow = selectionEdges ? buildSelectionBoxShadow(selectionEdges) : null;
11130
- const needsFocus = state === "focused" || state === "editing" || state === "readonlyFocused" || isAnchorInRange;
11213
+ const handleClick = (0, react.useCallback)((e) => {
11214
+ if (state === "editing") return;
11215
+ if (e.shiftKey) cell.selectTo();
11216
+ else cell.focus();
11217
+ }, [state, cell]);
11218
+ const handleMouseEnter = (0, react.useCallback)((e) => {
11219
+ if (e.buttons === 1) cell.dragExtend();
11220
+ }, [cell]);
11131
11221
  const handleBlur = (0, react.useCallback)((e) => {
11132
11222
  if (!cell.isFocused || cell.isEditing) return;
11133
- if (menuOpenRef.current) return;
11223
+ if (menuOpen) return;
11134
11224
  if (e.relatedTarget?.closest("[data-testid^=\"cell-\"]")) return;
11135
11225
  cell.blur();
11136
- }, [cell]);
11137
- const handleContextMenu = (0, react.useCallback)(() => {
11138
- if (!cell.isFocused && !cell.isInSelection) cell.focus();
11139
- }, [cell]);
11140
- const handleMenuOpenChange = (0, react.useCallback)((details) => {
11141
- menuOpenRef.current = details.open;
11142
- if (details.open) return;
11143
- if (deferredEdit.triggerIfRequested()) return;
11144
- if (cell.isEditing) return;
11145
- if (cell.isAnchor || cell.isFocused && !cell.hasRangeSelection) cellRef.current?.focus();
11146
- else ((cellRef.current?.closest("[data-testid=\"table-widget\"]"))?.querySelector("[data-testid^=\"cell-\"][tabindex=\"0\"]"))?.focus();
11147
- }, [cell, deferredEdit]);
11148
- const extraStyles = {};
11149
- if (isAnchorInRange) {
11150
- extraStyles._before = {
11151
- content: "\"\"",
11152
- position: "absolute",
11153
- inset: "1px",
11154
- border: "2px solid",
11155
- borderColor: "blue.400",
11156
- borderRadius: "1px",
11157
- pointerEvents: "none"
11158
- };
11159
- extraStyles._focus = FOCUS_RING_RESET;
11160
- extraStyles._focusVisible = FOCUS_RING_RESET;
11161
- }
11162
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Menu.Root, {
11163
- onOpenChange: handleMenuOpenChange,
11164
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Menu.ContextTrigger, {
11165
- asChild: true,
11166
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Box, {
11167
- ref: cellRef,
11168
- height: "40px",
11169
- px: "8px",
11170
- position: "relative",
11171
- overflow: "hidden",
11172
- onClick: handleClick,
11173
- onMouseDown: handleMouseDown,
11174
- onMouseEnter: handleMouseEnter,
11175
- onDoubleClick: handleDoubleClick,
11176
- onKeyDown: handleKeyDown,
11177
- onBlur: handleBlur,
11178
- onContextMenu: handleContextMenu,
11179
- tabIndex: needsFocus ? 0 : -1,
11180
- userSelect: state === "selected" ? "none" : void 0,
11181
- "data-testid": `cell-${cell.rowId}-${cell.field}`,
11182
- ...stateStyles[state],
11183
- ...extraStyles,
11184
- boxShadow: selectionShadow || void 0,
11185
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Box, {
11186
- display: "flex",
11187
- alignItems: "center",
11188
- height: "100%",
11189
- width: "100%",
11190
- minWidth: 0,
11191
- overflow: "hidden",
11192
- children
11193
- })
11194
- })
11195
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CellContextMenu, {
11196
- cell,
11197
- onEditPointerDown: deferredEdit.requestEdit
11198
- })]
11199
- });
11226
+ }, [cell, menuOpen]);
11227
+ const selectionShadow = selectionEdges ? buildSelectionBoxShadow(selectionEdges) : null;
11228
+ let className = STATE_CLASS[state];
11229
+ if (isAnchorInRange) className += ` ${CLS_ANCHOR}`;
11230
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
11231
+ ref: cellRef,
11232
+ className,
11233
+ style: selectionShadow ? { "--cw-shadow": selectionShadow } : void 0,
11234
+ tabIndex: isActive ? 0 : -1,
11235
+ onClick: handleClick,
11236
+ onMouseDown: handleMouseDown,
11237
+ onMouseEnter: handleMouseEnter,
11238
+ onDoubleClick: isActive ? handleDoubleClick : void 0,
11239
+ onKeyDown: isActive ? handleKeyDown : void 0,
11240
+ onBlur: isActive ? handleBlur : void 0,
11241
+ onContextMenu: preventContextMenu,
11242
+ "data-testid": `cell-${cell.rowId}-${cell.field}`,
11243
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
11244
+ style: INNER_STYLE,
11245
+ children
11246
+ })
11247
+ }), menuOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LazyContextMenu, {
11248
+ cell,
11249
+ cellRef,
11250
+ anchorRect: menuAnchor,
11251
+ onClose: handleMenuClose,
11252
+ onEditPointerDown: deferredEdit.requestEdit
11253
+ })] });
11200
11254
  });
11255
+ function preventContextMenu(e) {
11256
+ e.preventDefault();
11257
+ }
11201
11258
 
11202
11259
  //#endregion
11203
11260
  //#region src/table-editor/Table/ui/Cell/useTextareaCell.ts
@@ -11830,6 +11887,30 @@ function clearRange(range, rows, cols) {
11830
11887
  }
11831
11888
  }
11832
11889
 
11890
+ //#endregion
11891
+ //#region src/table-editor/Table/ui/borderConstants.ts
11892
+ const CELL_BORDER_COLOR = "#ededed";
11893
+ const BOTTOM_BORDER_SHADOW = `inset 0 -1px 0 0 ${CELL_BORDER_COLOR}`;
11894
+ function buildAddColumnShadowCss() {
11895
+ return { "&::after": {
11896
+ content: "\"\"",
11897
+ position: "absolute",
11898
+ top: 0,
11899
+ bottom: 0,
11900
+ width: "8px",
11901
+ left: "-8px",
11902
+ pointerEvents: "none",
11903
+ transition: "opacity 0.15s",
11904
+ opacity: "var(--shadow-right-opacity, 0)",
11905
+ boxShadow: "inset -8px 0 12px -8px rgba(0,0,0,0.1)"
11906
+ } };
11907
+ }
11908
+ function adjustRightOffsetCss(rightCss, addColOffset) {
11909
+ if (addColOffset <= 0) return rightCss;
11910
+ if (rightCss === "0px") return `${addColOffset}px`;
11911
+ return `calc(${rightCss} + ${addColOffset}px)`;
11912
+ }
11913
+
11833
11914
  //#endregion
11834
11915
  //#region src/table-editor/Table/ui/ResizeHandle.tsx
11835
11916
  const MIN_COLUMN_WIDTH = 40;
@@ -13018,12 +13099,20 @@ const TableWidget = (0, mobx_react_lite.observer)(({ rows, columnsModel, cellFSM
13018
13099
  if (isResizing) shadowModel.pause();
13019
13100
  else shadowModel.resume();
13020
13101
  }, [isResizing, shadowModel]);
13102
+ const [wrapperEl, setWrapperEl] = (0, react.useState)(null);
13021
13103
  const wrapperRefCallback = (0, react.useCallback)((el) => {
13104
+ setWrapperEl(el);
13022
13105
  columnsModel.setWrapperElement(el);
13023
- if (useWindowScrollProp) setScrollerRef(el);
13106
+ }, [columnsModel]);
13107
+ (0, react.useEffect)(() => {
13108
+ if (!useWindowScrollProp || !wrapperEl) return;
13109
+ setScrollerRef(wrapperEl);
13110
+ return () => {
13111
+ setScrollerRef(null);
13112
+ };
13024
13113
  }, [
13025
- columnsModel,
13026
13114
  useWindowScrollProp,
13115
+ wrapperEl,
13027
13116
  setScrollerRef
13028
13117
  ]);
13029
13118
  const handleSelectRow = (0, react.useCallback)((rowId) => {