@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.mjs CHANGED
@@ -7007,6 +7007,9 @@ function selectDefaultColumns(columns, maxVisible = 4) {
7007
7007
  //#region src/table-editor/Columns/model/ColumnsModel.ts
7008
7008
  const DEFAULT_COLUMN_WIDTH = 150;
7009
7009
  const DEFAULT_ID_COLUMN_WIDTH = 240;
7010
+ function fieldToCssVar(field) {
7011
+ return `--cw-${field.replaceAll(".", "-")}`;
7012
+ }
7010
7013
  function isValidPinZoneOrder(fields, pins) {
7011
7014
  let zone = "left";
7012
7015
  for (const field of fields) {
@@ -7196,7 +7199,7 @@ var ColumnsModel = class {
7196
7199
  setColumnWidth(field, width) {
7197
7200
  this._isResizing = true;
7198
7201
  this._columnWidths.set(field, width);
7199
- if (this._wrapperElement) this._wrapperElement.style.setProperty(`--cw-${field}`, `${width}px`);
7202
+ if (this._wrapperElement) this._wrapperElement.style.setProperty(fieldToCssVar(field), `${width}px`);
7200
7203
  }
7201
7204
  commitColumnWidth() {
7202
7205
  this._isResizing = false;
@@ -7216,7 +7219,7 @@ var ColumnsModel = class {
7216
7219
  return this._buildCssVarsUntracked();
7217
7220
  }
7218
7221
  columnWidthCssVar(field) {
7219
- return `var(--cw-${field}, ${field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH}px)`;
7222
+ return `var(${fieldToCssVar(field)}, ${field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH}px)`;
7220
7223
  }
7221
7224
  getPinState(field) {
7222
7225
  return this._pinnedColumns.get(field);
@@ -7443,7 +7446,7 @@ var ColumnsModel = class {
7443
7446
  const vars = {};
7444
7447
  for (const field of this._visibleFields) {
7445
7448
  const width = this._columnWidths.get(field) ?? (field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH);
7446
- vars[`--cw-${field}`] = `${width}px`;
7449
+ vars[fieldToCssVar(field)] = `${width}px`;
7447
7450
  }
7448
7451
  return vars;
7449
7452
  }
@@ -7452,7 +7455,7 @@ var ColumnsModel = class {
7452
7455
  const vars = {};
7453
7456
  for (const field of this._visibleFields) {
7454
7457
  const width = this._columnWidths.get(field) ?? (field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH);
7455
- vars[`--cw-${field}`] = `${width}px`;
7458
+ vars[fieldToCssVar(field)] = `${width}px`;
7456
7459
  }
7457
7460
  return vars;
7458
7461
  });
@@ -10825,6 +10828,242 @@ const CellContextMenu = observer(({ cell, onEditPointerDown }) => {
10825
10828
  }) }) });
10826
10829
  });
10827
10830
 
10831
+ //#endregion
10832
+ //#region src/table-editor/Table/ui/Cell/cellStyles.ts
10833
+ const SELECTION_BORDER_COLOR = "#3b82f6";
10834
+ function getCellState(cell) {
10835
+ if (cell.isReadOnly) {
10836
+ if (cell.isFocused && !cell.isInSelection) return "readonlyFocused";
10837
+ if (cell.isInSelection) return "selected";
10838
+ return "readonly";
10839
+ }
10840
+ if (cell.isEditing) return "editing";
10841
+ if (cell.isFocused && !cell.isInSelection) return "focused";
10842
+ if (cell.isInSelection) return "selected";
10843
+ return "display";
10844
+ }
10845
+ function isPrintableKey(e) {
10846
+ if (e.ctrlKey || e.metaKey || e.altKey) return false;
10847
+ return e.key.length === 1;
10848
+ }
10849
+
10850
+ //#endregion
10851
+ //#region src/table-editor/Table/ui/Cell/cellCss.ts
10852
+ const CLS = "cw";
10853
+ const CLS_DISPLAY = `${CLS} cw-display`;
10854
+ const CLS_READONLY = `${CLS} cw-readonly`;
10855
+ const CLS_FOCUSED = `${CLS} cw-focused`;
10856
+ const CLS_EDITING = `${CLS} cw-editing`;
10857
+ const CLS_READONLY_FOCUSED = `${CLS} cw-readonlyFocused`;
10858
+ const CLS_SELECTED = `${CLS} cw-selected`;
10859
+ const CLS_ANCHOR = "cw-anchor";
10860
+ const STATE_CLASS = {
10861
+ display: CLS_DISPLAY,
10862
+ readonly: CLS_READONLY,
10863
+ focused: CLS_FOCUSED,
10864
+ editing: CLS_EDITING,
10865
+ readonlyFocused: CLS_READONLY_FOCUSED,
10866
+ selected: CLS_SELECTED
10867
+ };
10868
+ const CELL_STYLE_ID = "cell-wrapper-styles";
10869
+ function ensureCellStyles() {
10870
+ if (typeof document === "undefined") return;
10871
+ if (document.getElementById(CELL_STYLE_ID)) return;
10872
+ const style = document.createElement("style");
10873
+ style.id = CELL_STYLE_ID;
10874
+ style.textContent = [
10875
+ ".cw{height:40px;padding:0 8px;position:relative;overflow:hidden;cursor:cell;box-shadow:var(--cw-shadow,none)}",
10876
+ ".cw:focus,.cw:focus-visible{outline:none;box-shadow:var(--cw-shadow,none)}",
10877
+ ".cw-display:hover,.cw-readonly:hover{background-color:var(--chakra-colors-gray-50);box-shadow:inset 0 -1px 0 0 #ededed}",
10878
+ ".cw-focused{background-color:var(--chakra-colors-blue-50)}",
10879
+ ".cw-focused::before,.cw-editing::before,.cw-readonlyFocused::before,.cw-anchor::before{content:\"\";position:absolute;inset:1px;border-radius:1px;pointer-events:none}",
10880
+ ".cw-focused::before{border:2px solid var(--chakra-colors-blue-400)}",
10881
+ ".cw-editing{cursor:text;background-color:white;z-index:1}",
10882
+ ".cw-editing::before{border:2px solid var(--chakra-colors-blue-500)}",
10883
+ ".cw-readonlyFocused{background-color:var(--chakra-colors-gray-50)}",
10884
+ ".cw-readonlyFocused::before{border:2px solid var(--chakra-colors-gray-400)}",
10885
+ ".cw-selected{background-color:var(--chakra-colors-blue-100);user-select:none}",
10886
+ ".cw-anchor::before{border:2px solid var(--chakra-colors-blue-400)}"
10887
+ ].join("");
10888
+ document.head.appendChild(style);
10889
+ }
10890
+ const INNER_STYLE = {
10891
+ display: "flex",
10892
+ alignItems: "center",
10893
+ height: "100%",
10894
+ width: "100%",
10895
+ minWidth: 0,
10896
+ overflow: "hidden"
10897
+ };
10898
+ function buildSelectionBoxShadow(edges) {
10899
+ const shadows = [];
10900
+ if (edges.top) shadows.push(`inset 0 2px 0 0 ${SELECTION_BORDER_COLOR}`);
10901
+ if (edges.bottom) shadows.push(`inset 0 -2px 0 0 ${SELECTION_BORDER_COLOR}`);
10902
+ if (edges.left) shadows.push(`inset 2px 0 0 0 ${SELECTION_BORDER_COLOR}`);
10903
+ if (edges.right) shadows.push(`inset -2px 0 0 0 ${SELECTION_BORDER_COLOR}`);
10904
+ return shadows.length > 0 ? shadows.join(", ") : null;
10905
+ }
10906
+
10907
+ //#endregion
10908
+ //#region src/table-editor/Table/ui/Cell/useCellContextMenu.ts
10909
+ let pendingContextMenu = null;
10910
+ function clearPendingContextMenu() {
10911
+ const pending = pendingContextMenu;
10912
+ pendingContextMenu = null;
10913
+ return pending;
10914
+ }
10915
+ function setPendingContextMenu(value) {
10916
+ pendingContextMenu = value;
10917
+ }
10918
+ const cellMenuRegistry = /* @__PURE__ */ new WeakMap();
10919
+ function useCellContextMenu(cell, cellRef, deferredEdit) {
10920
+ const [menuAnchor, setMenuAnchor] = useState(null);
10921
+ const menuOpen = menuAnchor !== null;
10922
+ const openContextMenuAt = useCallback((clientX, clientY) => {
10923
+ if (!cell.isFocused && !cell.isInSelection) cell.focus();
10924
+ setMenuAnchor(new DOMRect(clientX, clientY, 0, 0));
10925
+ }, [cell]);
10926
+ useEffect(() => {
10927
+ const el = cellRef.current;
10928
+ if (el) cellMenuRegistry.set(el, openContextMenuAt);
10929
+ return () => {
10930
+ if (el) cellMenuRegistry.delete(el);
10931
+ };
10932
+ }, [cellRef, openContextMenuAt]);
10933
+ const menuCloseRef = useRef(null);
10934
+ const handleMenuClose = useCallback(() => {
10935
+ const pending = clearPendingContextMenu();
10936
+ const didTriggerEdit = deferredEdit.triggerIfRequested();
10937
+ if (!pending && !didTriggerEdit && !cell.isEditing) if (cell.isAnchor || cell.isFocused && !cell.hasRangeSelection) cellRef.current?.focus();
10938
+ else ((cellRef.current?.closest("[data-testid=\"table-widget\"]"))?.querySelector("[data-testid^=\"cell-\"][tabindex=\"0\"]"))?.focus();
10939
+ setMenuAnchor(null);
10940
+ if (pending) {
10941
+ const openFn = cellMenuRegistry.get(pending.target);
10942
+ if (openFn) setTimeout(() => {
10943
+ openFn(pending.clientX, pending.clientY);
10944
+ }, 0);
10945
+ }
10946
+ }, [
10947
+ cell,
10948
+ cellRef,
10949
+ deferredEdit
10950
+ ]);
10951
+ menuCloseRef.current = handleMenuClose;
10952
+ return {
10953
+ menuAnchor,
10954
+ menuOpen,
10955
+ openContextMenuAt,
10956
+ handleMenuClose,
10957
+ menuCloseRef
10958
+ };
10959
+ }
10960
+
10961
+ //#endregion
10962
+ //#region src/table-editor/Table/ui/Cell/useCellFocus.ts
10963
+ function useCellFocus(cellRef, state, isAnchorInRange, navVersion) {
10964
+ useEffect(() => {
10965
+ if (!cellRef.current) return;
10966
+ if (state === "focused" || state === "readonlyFocused" || isAnchorInRange) cellRef.current.focus();
10967
+ else if (state === "display" || state === "readonly" || state === "selected") cellRef.current.blur();
10968
+ }, [
10969
+ cellRef,
10970
+ state,
10971
+ isAnchorInRange,
10972
+ navVersion
10973
+ ]);
10974
+ }
10975
+
10976
+ //#endregion
10977
+ //#region src/table-editor/Table/ui/Cell/useCellKeyboard.ts
10978
+ function handleArrowKey(cell, e, shiftAction, moveAction) {
10979
+ e.preventDefault();
10980
+ if (e.shiftKey) shiftAction();
10981
+ else if (cell.hasRangeSelection) cell.focus();
10982
+ else moveAction();
10983
+ }
10984
+ function handleEditableKeys(cell, e, callbacks) {
10985
+ if (cell.isReadOnly) {
10986
+ if (e.key === "Enter" || e.key === "Delete" || e.key === "Backspace" || isPrintableKey(e)) {
10987
+ e.preventDefault();
10988
+ cell.notifyReadonlyEditAttempt();
10989
+ }
10990
+ return;
10991
+ }
10992
+ const hasRange = cell.hasRangeSelection;
10993
+ if (!hasRange && e.key === "Enter") {
10994
+ e.preventDefault();
10995
+ if (callbacks.onStartEdit) callbacks.onStartEdit();
10996
+ else if (callbacks.onDoubleClick) callbacks.onDoubleClick();
10997
+ } else if (!hasRange && (e.key === "Delete" || e.key === "Backspace") && callbacks.onDelete) {
10998
+ e.preventDefault();
10999
+ callbacks.onDelete();
11000
+ } else if (isPrintableKey(e) && callbacks.onTypeChar) {
11001
+ e.preventDefault();
11002
+ callbacks.onTypeChar(e.key);
11003
+ }
11004
+ }
11005
+ function useCellKeyboard(cell, state, isAnchorInRange, menuOpen, menuCloseRef, callbacks) {
11006
+ const { onStartEdit, onDoubleClick, onTypeChar, onDelete } = callbacks;
11007
+ return {
11008
+ handleKeyDown: useCallback((e) => {
11009
+ if (menuOpen && e.key === "Escape") {
11010
+ e.preventDefault();
11011
+ menuCloseRef.current?.();
11012
+ return;
11013
+ }
11014
+ if (!(state === "focused" || state === "readonlyFocused" || isAnchorInRange)) return;
11015
+ const isMod = e.ctrlKey || e.metaKey;
11016
+ if (isMod && e.key === "c" && !cell.hasRangeSelection) {
11017
+ e.preventDefault();
11018
+ cell.copyToClipboard();
11019
+ return;
11020
+ }
11021
+ if (isMod && e.key === "v") return;
11022
+ const arrow = {
11023
+ ArrowUp: [cell.shiftMoveUp, cell.moveUp],
11024
+ ArrowDown: [cell.shiftMoveDown, cell.moveDown],
11025
+ ArrowLeft: [cell.shiftMoveLeft, cell.moveLeft],
11026
+ ArrowRight: [cell.shiftMoveRight, cell.moveRight]
11027
+ }[e.key];
11028
+ if (e.key === "Escape") {
11029
+ e.preventDefault();
11030
+ if (cell.hasRangeSelection) cell.focus();
11031
+ else cell.blur();
11032
+ } else if (arrow) handleArrowKey(cell, e, arrow[0], arrow[1]);
11033
+ else if (e.key === "Tab") {
11034
+ e.preventDefault();
11035
+ cell.handleTab(e.shiftKey);
11036
+ } else handleEditableKeys(cell, e, {
11037
+ onStartEdit,
11038
+ onDoubleClick,
11039
+ onTypeChar,
11040
+ onDelete
11041
+ });
11042
+ }, [
11043
+ state,
11044
+ cell,
11045
+ isAnchorInRange,
11046
+ menuOpen,
11047
+ menuCloseRef,
11048
+ onStartEdit,
11049
+ onDoubleClick,
11050
+ onTypeChar,
11051
+ onDelete
11052
+ ]),
11053
+ handleDoubleClick: useCallback((e) => {
11054
+ if (state === "readonly" || state === "readonlyFocused") {
11055
+ cell.notifyReadonlyEditAttempt();
11056
+ return;
11057
+ }
11058
+ onDoubleClick?.(e.clientX);
11059
+ }, [
11060
+ state,
11061
+ cell,
11062
+ onDoubleClick
11063
+ ])
11064
+ };
11065
+ }
11066
+
10828
11067
  //#endregion
10829
11068
  //#region src/table-editor/Table/ui/Cell/useDeferredMenuEdit.ts
10830
11069
  /**
@@ -10879,299 +11118,117 @@ function useDeferredMenuEdit(editFn) {
10879
11118
  };
10880
11119
  }
10881
11120
 
10882
- //#endregion
10883
- //#region src/table-editor/Table/ui/borderConstants.ts
10884
- const CELL_BORDER_COLOR = "#ededed";
10885
- const BOTTOM_BORDER_SHADOW = `inset 0 -1px 0 0 ${CELL_BORDER_COLOR}`;
10886
- function buildAddColumnShadowCss() {
10887
- return { "&::after": {
10888
- content: "\"\"",
10889
- position: "absolute",
10890
- top: 0,
10891
- bottom: 0,
10892
- width: "8px",
10893
- left: "-8px",
10894
- pointerEvents: "none",
10895
- transition: "opacity 0.15s",
10896
- opacity: "var(--shadow-right-opacity, 0)",
10897
- boxShadow: "inset -8px 0 12px -8px rgba(0,0,0,0.1)"
10898
- } };
10899
- }
10900
- function adjustRightOffsetCss(rightCss, addColOffset) {
10901
- if (addColOffset <= 0) return rightCss;
10902
- if (rightCss === "0px") return `${addColOffset}px`;
10903
- return `calc(${rightCss} + ${addColOffset}px)`;
10904
- }
10905
-
10906
- //#endregion
10907
- //#region src/table-editor/Table/ui/Cell/cellStyles.ts
10908
- const FOCUS_RING_RESET = {
10909
- outline: "none",
10910
- boxShadow: "none"
10911
- };
10912
- const SELECTION_BORDER_COLOR = "#3b82f6";
10913
- function buildSelectionBoxShadow(edges) {
10914
- const shadows = [];
10915
- if (edges.top) shadows.push(`inset 0 2px 0 0 ${SELECTION_BORDER_COLOR}`);
10916
- if (edges.bottom) shadows.push(`inset 0 -2px 0 0 ${SELECTION_BORDER_COLOR}`);
10917
- if (edges.left) shadows.push(`inset 2px 0 0 0 ${SELECTION_BORDER_COLOR}`);
10918
- if (edges.right) shadows.push(`inset -2px 0 0 0 ${SELECTION_BORDER_COLOR}`);
10919
- return shadows.length > 0 ? shadows.join(", ") : null;
10920
- }
10921
- const stateStyles = {
10922
- display: {
10923
- cursor: "cell",
10924
- _hover: {
10925
- bg: "gray.50",
10926
- boxShadow: BOTTOM_BORDER_SHADOW
10927
- }
10928
- },
10929
- focused: {
10930
- cursor: "cell",
10931
- bg: "blue.50",
10932
- _before: {
10933
- content: "\"\"",
10934
- position: "absolute",
10935
- inset: "1px",
10936
- border: "2px solid",
10937
- borderColor: "blue.400",
10938
- borderRadius: "1px",
10939
- pointerEvents: "none"
10940
- },
10941
- _focus: FOCUS_RING_RESET,
10942
- _focusVisible: FOCUS_RING_RESET
10943
- },
10944
- editing: {
10945
- cursor: "text",
10946
- bg: "white",
10947
- zIndex: 1,
10948
- _before: {
10949
- content: "\"\"",
10950
- position: "absolute",
10951
- inset: "1px",
10952
- border: "2px solid",
10953
- borderColor: "blue.500",
10954
- borderRadius: "1px",
10955
- pointerEvents: "none"
10956
- },
10957
- _focus: FOCUS_RING_RESET,
10958
- _focusVisible: FOCUS_RING_RESET
10959
- },
10960
- readonly: {
10961
- cursor: "cell",
10962
- _hover: {
10963
- bg: "gray.50",
10964
- boxShadow: BOTTOM_BORDER_SHADOW
10965
- }
10966
- },
10967
- readonlyFocused: {
10968
- cursor: "cell",
10969
- bg: "gray.50",
10970
- _before: {
10971
- content: "\"\"",
10972
- position: "absolute",
10973
- inset: "1px",
10974
- border: "2px solid",
10975
- borderColor: "gray.400",
10976
- borderRadius: "1px",
10977
- pointerEvents: "none"
10978
- },
10979
- _focus: FOCUS_RING_RESET,
10980
- _focusVisible: FOCUS_RING_RESET
10981
- },
10982
- selected: {
10983
- cursor: "cell",
10984
- bg: "blue.100"
10985
- }
10986
- };
10987
- function getCellState(cell) {
10988
- if (cell.isReadOnly) {
10989
- if (cell.isFocused && !cell.isInSelection) return "readonlyFocused";
10990
- if (cell.isInSelection) return "selected";
10991
- return "readonly";
10992
- }
10993
- if (cell.isEditing) return "editing";
10994
- if (cell.isFocused && !cell.isInSelection) return "focused";
10995
- if (cell.isInSelection) return "selected";
10996
- return "display";
10997
- }
10998
- function isPrintableKey(e) {
10999
- if (e.ctrlKey || e.metaKey || e.altKey) return false;
11000
- return e.key.length === 1;
11001
- }
11002
-
11003
11121
  //#endregion
11004
11122
  //#region src/table-editor/Table/ui/Cell/CellWrapper.tsx
11005
- function handleArrowKey(cell, e, shiftAction, moveAction) {
11006
- e.preventDefault();
11007
- if (e.shiftKey) shiftAction();
11008
- else if (cell.hasRangeSelection) cell.focus();
11009
- else moveAction();
11010
- }
11011
- function handleEditableKeys(cell, e, onStartEdit, onDoubleClick, onTypeChar, onDelete) {
11012
- if (cell.isReadOnly) {
11013
- if (e.key === "Enter" || e.key === "Delete" || e.key === "Backspace" || isPrintableKey(e)) cell.notifyReadonlyEditAttempt();
11014
- return;
11015
- }
11016
- const hasRange = cell.hasRangeSelection;
11017
- if (!hasRange && e.key === "Enter") {
11018
- e.preventDefault();
11019
- if (onStartEdit) onStartEdit();
11020
- else if (onDoubleClick) onDoubleClick();
11021
- } else if (!hasRange && (e.key === "Delete" || e.key === "Backspace") && onDelete) {
11022
- e.preventDefault();
11023
- onDelete();
11024
- } else if (isPrintableKey(e) && onTypeChar) {
11025
- e.preventDefault();
11026
- onTypeChar(e.key);
11027
- }
11028
- }
11123
+ const LazyContextMenu = observer(({ cell, cellRef, anchorRect, onClose, onEditPointerDown }) => {
11124
+ const handleOpenChange = useCallback((details) => {
11125
+ if (!details.open) onClose();
11126
+ }, [onClose]);
11127
+ const handleInteractOutside = useCallback((e) => {
11128
+ const originalEvent = e.detail?.originalEvent;
11129
+ if (!originalEvent || originalEvent.button !== 2) return;
11130
+ const targetCell = originalEvent.target?.closest("[data-testid^=\"cell-\"]");
11131
+ if (targetCell) setPendingContextMenu({
11132
+ target: targetCell,
11133
+ clientX: originalEvent.clientX,
11134
+ clientY: originalEvent.clientY
11135
+ });
11136
+ }, []);
11137
+ const getAnchorRect = useCallback(() => {
11138
+ return anchorRect ?? (cellRef.current?.getBoundingClientRect() || null);
11139
+ }, [anchorRect, cellRef]);
11140
+ return /* @__PURE__ */ jsx(Menu.Root, {
11141
+ open: true,
11142
+ onOpenChange: handleOpenChange,
11143
+ onInteractOutside: handleInteractOutside,
11144
+ positioning: {
11145
+ placement: "bottom-start",
11146
+ getAnchorRect
11147
+ },
11148
+ children: /* @__PURE__ */ jsx(CellContextMenu, {
11149
+ cell,
11150
+ onEditPointerDown
11151
+ })
11152
+ });
11153
+ });
11029
11154
  const CellWrapper = observer(({ cell, children, onDoubleClick, onStartEdit, onTypeChar, onDelete }) => {
11155
+ ensureCellStyles();
11030
11156
  const cellRef = useRef(null);
11031
- const menuOpenRef = useRef(false);
11032
- const deferredEdit = useDeferredMenuEdit(onStartEdit ?? onDoubleClick);
11033
11157
  const state = getCellState(cell);
11034
11158
  const selectionEdges = cell.selectionEdges;
11035
11159
  const isAnchorInRange = cell.isAnchor && cell.isInSelection;
11036
11160
  const navVersion = cell.navigationVersion;
11037
- useEffect(() => {
11038
- if (!cellRef.current) return;
11039
- if (state === "focused" || state === "readonlyFocused" || isAnchorInRange) cellRef.current.focus();
11040
- else if (state === "display" || state === "readonly" || state === "selected") cellRef.current.blur();
11041
- }, [
11042
- state,
11043
- isAnchorInRange,
11044
- navVersion
11045
- ]);
11046
- const handleClick = useCallback((e) => {
11047
- if (state === "editing") return;
11048
- if (e.shiftKey) cell.selectTo();
11049
- else cell.focus();
11050
- }, [state, cell]);
11161
+ const isActive = state === "focused" || state === "editing" || state === "readonlyFocused" || isAnchorInRange;
11162
+ const deferredEdit = useDeferredMenuEdit(onStartEdit ?? onDoubleClick);
11163
+ const { menuAnchor, menuOpen, openContextMenuAt, handleMenuClose, menuCloseRef } = useCellContextMenu(cell, cellRef, deferredEdit);
11164
+ useCellFocus(cellRef, state, isAnchorInRange, navVersion);
11165
+ const { handleKeyDown, handleDoubleClick } = useCellKeyboard(cell, state, isAnchorInRange, menuOpen, menuCloseRef, {
11166
+ onStartEdit,
11167
+ onDoubleClick,
11168
+ onTypeChar,
11169
+ onDelete
11170
+ });
11051
11171
  const handleMouseDown = useCallback((e) => {
11052
11172
  if (e.detail === 2 && state !== "readonly" && state !== "readonlyFocused") e.preventDefault();
11173
+ if (e.button === 2) {
11174
+ openContextMenuAt(e.clientX, e.clientY);
11175
+ return;
11176
+ }
11053
11177
  if (!e.shiftKey && e.button === 0 && state !== "editing") {
11054
11178
  e.preventDefault();
11055
11179
  cell.dragStart();
11056
11180
  cellRef.current?.focus();
11057
11181
  }
11058
- }, [state, cell]);
11059
- const handleMouseEnter = useCallback((e) => {
11060
- if (e.buttons === 1) cell.dragExtend();
11061
- }, [cell]);
11062
- const handleDoubleClick = useCallback((e) => {
11063
- if (state === "readonly" || state === "readonlyFocused") {
11064
- cell.notifyReadonlyEditAttempt();
11065
- return;
11066
- }
11067
- onDoubleClick?.(e.clientX);
11068
11182
  }, [
11069
11183
  state,
11070
11184
  cell,
11071
- onDoubleClick
11185
+ openContextMenuAt
11072
11186
  ]);
11073
- const handleKeyDown = useCallback((e) => {
11074
- if (!(state === "focused" || state === "readonlyFocused" || isAnchorInRange)) return;
11075
- const isMod = e.ctrlKey || e.metaKey;
11076
- if (isMod && e.key === "c" && !cell.hasRangeSelection) {
11077
- e.preventDefault();
11078
- cell.copyToClipboard();
11079
- return;
11080
- }
11081
- if (isMod && e.key === "v") return;
11082
- if (e.key === "Escape") {
11083
- e.preventDefault();
11084
- if (cell.hasRangeSelection) cell.focus();
11085
- else cell.blur();
11086
- } else if (e.key === "ArrowUp") handleArrowKey(cell, e, cell.shiftMoveUp, cell.moveUp);
11087
- else if (e.key === "ArrowDown") handleArrowKey(cell, e, cell.shiftMoveDown, cell.moveDown);
11088
- else if (e.key === "ArrowLeft") handleArrowKey(cell, e, cell.shiftMoveLeft, cell.moveLeft);
11089
- else if (e.key === "ArrowRight") handleArrowKey(cell, e, cell.shiftMoveRight, cell.moveRight);
11090
- else if (e.key === "Tab") {
11091
- e.preventDefault();
11092
- cell.handleTab(e.shiftKey);
11093
- } else handleEditableKeys(cell, e, onStartEdit, onDoubleClick, onTypeChar, onDelete);
11094
- }, [
11095
- state,
11096
- cell,
11097
- isAnchorInRange,
11098
- onStartEdit,
11099
- onDoubleClick,
11100
- onTypeChar,
11101
- onDelete
11102
- ]);
11103
- const selectionShadow = selectionEdges ? buildSelectionBoxShadow(selectionEdges) : null;
11104
- const needsFocus = state === "focused" || state === "editing" || state === "readonlyFocused" || isAnchorInRange;
11187
+ const handleClick = useCallback((e) => {
11188
+ if (state === "editing") return;
11189
+ if (e.shiftKey) cell.selectTo();
11190
+ else cell.focus();
11191
+ }, [state, cell]);
11192
+ const handleMouseEnter = useCallback((e) => {
11193
+ if (e.buttons === 1) cell.dragExtend();
11194
+ }, [cell]);
11105
11195
  const handleBlur = useCallback((e) => {
11106
11196
  if (!cell.isFocused || cell.isEditing) return;
11107
- if (menuOpenRef.current) return;
11197
+ if (menuOpen) return;
11108
11198
  if (e.relatedTarget?.closest("[data-testid^=\"cell-\"]")) return;
11109
11199
  cell.blur();
11110
- }, [cell]);
11111
- const handleContextMenu = useCallback(() => {
11112
- if (!cell.isFocused && !cell.isInSelection) cell.focus();
11113
- }, [cell]);
11114
- const handleMenuOpenChange = useCallback((details) => {
11115
- menuOpenRef.current = details.open;
11116
- if (details.open) return;
11117
- if (deferredEdit.triggerIfRequested()) return;
11118
- if (cell.isEditing) return;
11119
- if (cell.isAnchor || cell.isFocused && !cell.hasRangeSelection) cellRef.current?.focus();
11120
- else ((cellRef.current?.closest("[data-testid=\"table-widget\"]"))?.querySelector("[data-testid^=\"cell-\"][tabindex=\"0\"]"))?.focus();
11121
- }, [cell, deferredEdit]);
11122
- const extraStyles = {};
11123
- if (isAnchorInRange) {
11124
- extraStyles._before = {
11125
- content: "\"\"",
11126
- position: "absolute",
11127
- inset: "1px",
11128
- border: "2px solid",
11129
- borderColor: "blue.400",
11130
- borderRadius: "1px",
11131
- pointerEvents: "none"
11132
- };
11133
- extraStyles._focus = FOCUS_RING_RESET;
11134
- extraStyles._focusVisible = FOCUS_RING_RESET;
11135
- }
11136
- return /* @__PURE__ */ jsxs(Menu.Root, {
11137
- onOpenChange: handleMenuOpenChange,
11138
- children: [/* @__PURE__ */ jsx(Menu.ContextTrigger, {
11139
- asChild: true,
11140
- children: /* @__PURE__ */ jsx(Box, {
11141
- ref: cellRef,
11142
- height: "40px",
11143
- px: "8px",
11144
- position: "relative",
11145
- overflow: "hidden",
11146
- onClick: handleClick,
11147
- onMouseDown: handleMouseDown,
11148
- onMouseEnter: handleMouseEnter,
11149
- onDoubleClick: handleDoubleClick,
11150
- onKeyDown: handleKeyDown,
11151
- onBlur: handleBlur,
11152
- onContextMenu: handleContextMenu,
11153
- tabIndex: needsFocus ? 0 : -1,
11154
- userSelect: state === "selected" ? "none" : void 0,
11155
- "data-testid": `cell-${cell.rowId}-${cell.field}`,
11156
- ...stateStyles[state],
11157
- ...extraStyles,
11158
- boxShadow: selectionShadow || void 0,
11159
- children: /* @__PURE__ */ jsx(Box, {
11160
- display: "flex",
11161
- alignItems: "center",
11162
- height: "100%",
11163
- width: "100%",
11164
- minWidth: 0,
11165
- overflow: "hidden",
11166
- children
11167
- })
11168
- })
11169
- }), /* @__PURE__ */ jsx(CellContextMenu, {
11170
- cell,
11171
- onEditPointerDown: deferredEdit.requestEdit
11172
- })]
11173
- });
11200
+ }, [cell, menuOpen]);
11201
+ const selectionShadow = selectionEdges ? buildSelectionBoxShadow(selectionEdges) : null;
11202
+ let className = STATE_CLASS[state];
11203
+ if (isAnchorInRange) className += ` ${CLS_ANCHOR}`;
11204
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
11205
+ ref: cellRef,
11206
+ className,
11207
+ style: selectionShadow ? { "--cw-shadow": selectionShadow } : void 0,
11208
+ tabIndex: isActive ? 0 : -1,
11209
+ onClick: handleClick,
11210
+ onMouseDown: handleMouseDown,
11211
+ onMouseEnter: handleMouseEnter,
11212
+ onDoubleClick: isActive ? handleDoubleClick : void 0,
11213
+ onKeyDown: isActive ? handleKeyDown : void 0,
11214
+ onBlur: isActive ? handleBlur : void 0,
11215
+ onContextMenu: preventContextMenu,
11216
+ "data-testid": `cell-${cell.rowId}-${cell.field}`,
11217
+ children: /* @__PURE__ */ jsx("div", {
11218
+ style: INNER_STYLE,
11219
+ children
11220
+ })
11221
+ }), menuOpen && /* @__PURE__ */ jsx(LazyContextMenu, {
11222
+ cell,
11223
+ cellRef,
11224
+ anchorRect: menuAnchor,
11225
+ onClose: handleMenuClose,
11226
+ onEditPointerDown: deferredEdit.requestEdit
11227
+ })] });
11174
11228
  });
11229
+ function preventContextMenu(e) {
11230
+ e.preventDefault();
11231
+ }
11175
11232
 
11176
11233
  //#endregion
11177
11234
  //#region src/table-editor/Table/ui/Cell/useTextareaCell.ts
@@ -11804,6 +11861,30 @@ function clearRange(range, rows, cols) {
11804
11861
  }
11805
11862
  }
11806
11863
 
11864
+ //#endregion
11865
+ //#region src/table-editor/Table/ui/borderConstants.ts
11866
+ const CELL_BORDER_COLOR = "#ededed";
11867
+ const BOTTOM_BORDER_SHADOW = `inset 0 -1px 0 0 ${CELL_BORDER_COLOR}`;
11868
+ function buildAddColumnShadowCss() {
11869
+ return { "&::after": {
11870
+ content: "\"\"",
11871
+ position: "absolute",
11872
+ top: 0,
11873
+ bottom: 0,
11874
+ width: "8px",
11875
+ left: "-8px",
11876
+ pointerEvents: "none",
11877
+ transition: "opacity 0.15s",
11878
+ opacity: "var(--shadow-right-opacity, 0)",
11879
+ boxShadow: "inset -8px 0 12px -8px rgba(0,0,0,0.1)"
11880
+ } };
11881
+ }
11882
+ function adjustRightOffsetCss(rightCss, addColOffset) {
11883
+ if (addColOffset <= 0) return rightCss;
11884
+ if (rightCss === "0px") return `${addColOffset}px`;
11885
+ return `calc(${rightCss} + ${addColOffset}px)`;
11886
+ }
11887
+
11807
11888
  //#endregion
11808
11889
  //#region src/table-editor/Table/ui/ResizeHandle.tsx
11809
11890
  const MIN_COLUMN_WIDTH = 40;
@@ -12992,12 +13073,20 @@ const TableWidget = observer(({ rows, columnsModel, cellFSM, selection, sortMode
12992
13073
  if (isResizing) shadowModel.pause();
12993
13074
  else shadowModel.resume();
12994
13075
  }, [isResizing, shadowModel]);
13076
+ const [wrapperEl, setWrapperEl] = useState(null);
12995
13077
  const wrapperRefCallback = useCallback((el) => {
13078
+ setWrapperEl(el);
12996
13079
  columnsModel.setWrapperElement(el);
12997
- if (useWindowScrollProp) setScrollerRef(el);
13080
+ }, [columnsModel]);
13081
+ useEffect(() => {
13082
+ if (!useWindowScrollProp || !wrapperEl) return;
13083
+ setScrollerRef(wrapperEl);
13084
+ return () => {
13085
+ setScrollerRef(null);
13086
+ };
12998
13087
  }, [
12999
- columnsModel,
13000
13088
  useWindowScrollProp,
13089
+ wrapperEl,
13001
13090
  setScrollerRef
13002
13091
  ]);
13003
13092
  const handleSelectRow = useCallback((rowId) => {