@revisium/schema-toolkit-ui 0.6.0 → 0.6.2

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
@@ -6920,6 +6920,88 @@ const SYSTEM_FIELD_IDS = new Set([
6920
6920
  "schemaHash"
6921
6921
  ]);
6922
6922
 
6923
+ //#endregion
6924
+ //#region src/table-editor/TableEditor/model/SchemaContext.ts
6925
+ const DATA_FIELD = "data";
6926
+ function stripDataFieldPrefix(field) {
6927
+ const prefix = `${DATA_FIELD}.`;
6928
+ if (field.startsWith(prefix)) return field.slice(prefix.length);
6929
+ if (field === DATA_FIELD) return "";
6930
+ return field;
6931
+ }
6932
+ const SYSTEM_REF_SCHEMAS = {
6933
+ [_revisium_schema_toolkit.SystemSchemaIds.File]: _revisium_schema_toolkit.fileSchema,
6934
+ [_revisium_schema_toolkit.SystemSchemaIds.RowId]: _revisium_schema_toolkit.rowIdSchema,
6935
+ [_revisium_schema_toolkit.SystemSchemaIds.RowCreatedAt]: _revisium_schema_toolkit.rowCreatedAtSchema,
6936
+ [_revisium_schema_toolkit.SystemSchemaIds.RowCreatedId]: _revisium_schema_toolkit.rowCreatedIdSchema,
6937
+ [_revisium_schema_toolkit.SystemSchemaIds.RowVersionId]: _revisium_schema_toolkit.rowVersionIdSchema,
6938
+ [_revisium_schema_toolkit.SystemSchemaIds.RowPublishedAt]: _revisium_schema_toolkit.rowPublishedAtSchema,
6939
+ [_revisium_schema_toolkit.SystemSchemaIds.RowUpdatedAt]: _revisium_schema_toolkit.rowUpdatedAtSchema,
6940
+ [_revisium_schema_toolkit.SystemSchemaIds.RowHash]: _revisium_schema_toolkit.rowHashSchema,
6941
+ [_revisium_schema_toolkit.SystemSchemaIds.RowSchemaHash]: _revisium_schema_toolkit.rowSchemaHashSchema
6942
+ };
6943
+ function wrapDataSchema(dataSchema) {
6944
+ return {
6945
+ type: "object",
6946
+ properties: { [DATA_FIELD]: dataSchema },
6947
+ additionalProperties: false,
6948
+ required: [DATA_FIELD]
6949
+ };
6950
+ }
6951
+ function buildRowSchema(wrappedDataSchema) {
6952
+ const systemProperties = {};
6953
+ for (const sf of SYSTEM_FIELDS) systemProperties[sf.id] = { $ref: sf.ref };
6954
+ return {
6955
+ type: "object",
6956
+ properties: {
6957
+ ...systemProperties,
6958
+ ...wrappedDataSchema.properties
6959
+ },
6960
+ additionalProperties: false,
6961
+ required: [...Object.keys(systemProperties), ...wrappedDataSchema.required]
6962
+ };
6963
+ }
6964
+ var SchemaContext = class {
6965
+ _allColumns = [];
6966
+ _dataSchema = null;
6967
+ _wrappedDataSchema = null;
6968
+ _fullRefSchemas = {};
6969
+ _rootNode = null;
6970
+ get allColumns() {
6971
+ return this._allColumns;
6972
+ }
6973
+ get sortableFields() {
6974
+ return this._allColumns.filter((col) => !col.isDeprecated && col.fieldType !== FilterFieldType.File);
6975
+ }
6976
+ get filterableFields() {
6977
+ return this.sortableFields;
6978
+ }
6979
+ get dataSchema() {
6980
+ return this._dataSchema;
6981
+ }
6982
+ get wrappedDataSchema() {
6983
+ return this._wrappedDataSchema;
6984
+ }
6985
+ get fullRefSchemas() {
6986
+ return this._fullRefSchemas;
6987
+ }
6988
+ get rootNode() {
6989
+ return this._rootNode;
6990
+ }
6991
+ init(dataSchema, refSchemas) {
6992
+ this._dataSchema = dataSchema;
6993
+ this._fullRefSchemas = {
6994
+ ...SYSTEM_REF_SCHEMAS,
6995
+ ...refSchemas
6996
+ };
6997
+ const wrapped = wrapDataSchema(dataSchema);
6998
+ this._wrappedDataSchema = wrapped;
6999
+ const rowSchema = buildRowSchema(wrapped);
7000
+ this._rootNode = new _revisium_schema_toolkit.SchemaParser().parse(rowSchema, this._fullRefSchemas);
7001
+ this._allColumns = extractColumns(this._rootNode);
7002
+ }
7003
+ };
7004
+
6923
7005
  //#endregion
6924
7006
  //#region src/table-editor/Columns/model/extractColumns.ts
6925
7007
  const NODE_TYPE_TO_FIELD_TYPE = {
@@ -6963,15 +7045,20 @@ function resolveRefColumn(child, fieldPath) {
6963
7045
  const refValue = child.ref();
6964
7046
  if (!refValue) return null;
6965
7047
  const systemDef = SYSTEM_FIELD_BY_REF.get(refValue);
6966
- if (systemDef) return {
6967
- field: systemDef.id,
6968
- label: systemDef.label,
6969
- fieldType: systemDef.fieldType,
6970
- isSystem: true,
6971
- systemFieldId: systemDef.id,
6972
- isDeprecated: child.metadata().deprecated ?? false,
6973
- hasFormula: child.hasFormula()
6974
- };
7048
+ if (systemDef) {
7049
+ const isDeprecated = child.metadata().deprecated ?? false;
7050
+ const hasFormula = child.hasFormula();
7051
+ return {
7052
+ field: systemDef.id,
7053
+ label: systemDef.label,
7054
+ fieldType: systemDef.fieldType,
7055
+ isSystem: true,
7056
+ systemFieldId: systemDef.id,
7057
+ isDeprecated,
7058
+ hasFormula,
7059
+ isSortable: !isDeprecated && !hasFormula
7060
+ };
7061
+ }
6975
7062
  if (refValue === _revisium_schema_toolkit.SystemSchemaIds.File) return resolveFileRefColumns(child, fieldPath);
6976
7063
  return null;
6977
7064
  }
@@ -6984,14 +7071,22 @@ function resolveFileRefColumns(child, fieldPath) {
6984
7071
  }
6985
7072
  return result;
6986
7073
  }
7074
+ function stripDataPrefix(fieldPath) {
7075
+ if (fieldPath === DATA_FIELD) return DATA_FIELD;
7076
+ if (fieldPath.startsWith(`${DATA_FIELD}.`)) return fieldPath.slice(DATA_FIELD.length + 1);
7077
+ return fieldPath;
7078
+ }
6987
7079
  function createColumn(fieldPath, child, fieldType) {
7080
+ const isDeprecated = child.metadata().deprecated ?? false;
7081
+ const hasFormula = child.hasFormula();
6988
7082
  return {
6989
7083
  field: fieldPath,
6990
- label: fieldPath,
7084
+ label: stripDataPrefix(fieldPath),
6991
7085
  fieldType,
6992
7086
  isSystem: false,
6993
- isDeprecated: child.metadata().deprecated ?? false,
6994
- hasFormula: child.hasFormula()
7087
+ isDeprecated,
7088
+ hasFormula,
7089
+ isSortable: !isDeprecated && !hasFormula && fieldType !== FilterFieldType.File
6995
7090
  };
6996
7091
  }
6997
7092
 
@@ -7033,6 +7128,9 @@ function selectDefaultColumns(columns, maxVisible = 4) {
7033
7128
  //#region src/table-editor/Columns/model/ColumnsModel.ts
7034
7129
  const DEFAULT_COLUMN_WIDTH = 150;
7035
7130
  const DEFAULT_ID_COLUMN_WIDTH = 240;
7131
+ function fieldToCssVar(field) {
7132
+ return `--cw-${field.replaceAll(".", "-")}`;
7133
+ }
7036
7134
  function isValidPinZoneOrder(fields, pins) {
7037
7135
  let zone = "left";
7038
7136
  for (const field of fields) {
@@ -7075,6 +7173,10 @@ var ColumnsModel = class {
7075
7173
  const visible = this._visibleFieldSet;
7076
7174
  return this._allColumns.filter((col) => !visible.has(col.field) && !col.isSystem);
7077
7175
  }
7176
+ get availableFieldsToInsert() {
7177
+ const visible = this._visibleFieldSet;
7178
+ return this._allColumns.filter((col) => !visible.has(col.field));
7179
+ }
7078
7180
  get availableSystemFieldsToAdd() {
7079
7181
  const visible = this._visibleFieldSet;
7080
7182
  return this._allColumns.filter((col) => !visible.has(col.field) && col.isSystem);
@@ -7222,7 +7324,7 @@ var ColumnsModel = class {
7222
7324
  setColumnWidth(field, width) {
7223
7325
  this._isResizing = true;
7224
7326
  this._columnWidths.set(field, width);
7225
- if (this._wrapperElement) this._wrapperElement.style.setProperty(`--cw-${field}`, `${width}px`);
7327
+ if (this._wrapperElement) this._wrapperElement.style.setProperty(fieldToCssVar(field), `${width}px`);
7226
7328
  }
7227
7329
  commitColumnWidth() {
7228
7330
  this._isResizing = false;
@@ -7242,7 +7344,7 @@ var ColumnsModel = class {
7242
7344
  return this._buildCssVarsUntracked();
7243
7345
  }
7244
7346
  columnWidthCssVar(field) {
7245
- return `var(--cw-${field}, ${field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH}px)`;
7347
+ return `var(${fieldToCssVar(field)}, ${field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH}px)`;
7246
7348
  }
7247
7349
  getPinState(field) {
7248
7350
  return this._pinnedColumns.get(field);
@@ -7455,11 +7557,9 @@ var ColumnsModel = class {
7455
7557
  return this._pinnedColumns.get(neighbor) === pin;
7456
7558
  }
7457
7559
  _toViewField(field) {
7458
- if (this._columnLookup.get(field)?.isSystem) return field;
7459
- return `data.${field}`;
7560
+ return field;
7460
7561
  }
7461
7562
  _fromViewField(viewField) {
7462
- if (viewField.startsWith("data.")) return viewField.slice(5);
7463
7563
  return viewField;
7464
7564
  }
7465
7565
  _resolveWidthUntracked(field) {
@@ -7469,7 +7569,7 @@ var ColumnsModel = class {
7469
7569
  const vars = {};
7470
7570
  for (const field of this._visibleFields) {
7471
7571
  const width = this._columnWidths.get(field) ?? (field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH);
7472
- vars[`--cw-${field}`] = `${width}px`;
7572
+ vars[fieldToCssVar(field)] = `${width}px`;
7473
7573
  }
7474
7574
  return vars;
7475
7575
  }
@@ -7478,7 +7578,7 @@ var ColumnsModel = class {
7478
7578
  const vars = {};
7479
7579
  for (const field of this._visibleFields) {
7480
7580
  const width = this._columnWidths.get(field) ?? (field === "id" ? DEFAULT_ID_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH);
7481
- vars[`--cw-${field}`] = `${width}px`;
7581
+ vars[fieldToCssVar(field)] = `${width}px`;
7482
7582
  }
7483
7583
  return vars;
7484
7584
  });
@@ -7978,7 +8078,7 @@ function buildConditionClause(condition) {
7978
8078
  searchType: condition.searchType || "plain"
7979
8079
  } };
7980
8080
  return { data: {
7981
- path: condition.field,
8081
+ path: stripDataFieldPrefix(condition.field),
7982
8082
  search: condition.value,
7983
8083
  searchLanguage: condition.searchLanguage || "simple",
7984
8084
  searchType: condition.searchType || "plain"
@@ -7987,7 +8087,7 @@ function buildConditionClause(condition) {
7987
8087
  const opClause = buildOperatorClause(condition.operator, condition.value, condition.fieldType);
7988
8088
  if (SYSTEM_FIELD_IDS.has(condition.field)) return { [condition.field]: opClause };
7989
8089
  return { data: {
7990
- path: condition.field,
8090
+ path: stripDataFieldPrefix(condition.field),
7991
8091
  ...opClause
7992
8092
  } };
7993
8093
  }
@@ -9313,22 +9413,18 @@ var SortModel = class {
9313
9413
  if (firstAvailable) this.addSort(firstAvailable.field);
9314
9414
  }
9315
9415
  serializeToViewSorts() {
9316
- const lookup = this._fieldLookup;
9317
- return this.sorts.map((sort) => {
9318
- return {
9319
- field: lookup.get(sort.field)?.isSystem ? sort.field : `data.${sort.field}`,
9320
- direction: sort.direction
9321
- };
9322
- });
9416
+ return this.sorts.map((sort) => ({
9417
+ field: sort.field,
9418
+ direction: sort.direction
9419
+ }));
9323
9420
  }
9324
9421
  applyViewSorts(viewSorts) {
9325
9422
  const lookup = this._fieldLookup;
9326
9423
  const sorts = [];
9327
9424
  for (const vs of viewSorts) {
9328
- const field = vs.field.startsWith("data.") ? vs.field.slice(5) : vs.field;
9329
9425
  const direction = vs.direction === "desc" ? "desc" : "asc";
9330
- if (lookup.has(field)) sorts.push({
9331
- field,
9426
+ if (lookup.has(vs.field)) sorts.push({
9427
+ field: vs.field,
9332
9428
  direction
9333
9429
  });
9334
9430
  }
@@ -10851,6 +10947,242 @@ const CellContextMenu = (0, mobx_react_lite.observer)(({ cell, onEditPointerDown
10851
10947
  }) }) });
10852
10948
  });
10853
10949
 
10950
+ //#endregion
10951
+ //#region src/table-editor/Table/ui/Cell/cellStyles.ts
10952
+ const SELECTION_BORDER_COLOR = "#3b82f6";
10953
+ function getCellState(cell) {
10954
+ if (cell.isReadOnly) {
10955
+ if (cell.isFocused && !cell.isInSelection) return "readonlyFocused";
10956
+ if (cell.isInSelection) return "selected";
10957
+ return "readonly";
10958
+ }
10959
+ if (cell.isEditing) return "editing";
10960
+ if (cell.isFocused && !cell.isInSelection) return "focused";
10961
+ if (cell.isInSelection) return "selected";
10962
+ return "display";
10963
+ }
10964
+ function isPrintableKey(e) {
10965
+ if (e.ctrlKey || e.metaKey || e.altKey) return false;
10966
+ return e.key.length === 1;
10967
+ }
10968
+
10969
+ //#endregion
10970
+ //#region src/table-editor/Table/ui/Cell/cellCss.ts
10971
+ const CLS = "cw";
10972
+ const CLS_DISPLAY = `${CLS} cw-display`;
10973
+ const CLS_READONLY = `${CLS} cw-readonly`;
10974
+ const CLS_FOCUSED = `${CLS} cw-focused`;
10975
+ const CLS_EDITING = `${CLS} cw-editing`;
10976
+ const CLS_READONLY_FOCUSED = `${CLS} cw-readonlyFocused`;
10977
+ const CLS_SELECTED = `${CLS} cw-selected`;
10978
+ const CLS_ANCHOR = "cw-anchor";
10979
+ const STATE_CLASS = {
10980
+ display: CLS_DISPLAY,
10981
+ readonly: CLS_READONLY,
10982
+ focused: CLS_FOCUSED,
10983
+ editing: CLS_EDITING,
10984
+ readonlyFocused: CLS_READONLY_FOCUSED,
10985
+ selected: CLS_SELECTED
10986
+ };
10987
+ const CELL_STYLE_ID = "cell-wrapper-styles";
10988
+ function ensureCellStyles() {
10989
+ if (typeof document === "undefined") return;
10990
+ if (document.getElementById(CELL_STYLE_ID)) return;
10991
+ const style = document.createElement("style");
10992
+ style.id = CELL_STYLE_ID;
10993
+ style.textContent = [
10994
+ ".cw{height:40px;padding:0 8px;position:relative;overflow:hidden;cursor:cell;box-shadow:var(--cw-shadow,none)}",
10995
+ ".cw:focus,.cw:focus-visible{outline:none;box-shadow:var(--cw-shadow,none)}",
10996
+ ".cw-display:hover,.cw-readonly:hover{background-color:var(--chakra-colors-gray-50);box-shadow:inset 0 -1px 0 0 #ededed}",
10997
+ ".cw-focused{background-color:var(--chakra-colors-blue-50)}",
10998
+ ".cw-focused::before,.cw-editing::before,.cw-readonlyFocused::before,.cw-anchor::before{content:\"\";position:absolute;inset:1px;border-radius:1px;pointer-events:none}",
10999
+ ".cw-focused::before{border:2px solid var(--chakra-colors-blue-400)}",
11000
+ ".cw-editing{cursor:text;background-color:white;z-index:1}",
11001
+ ".cw-editing::before{border:2px solid var(--chakra-colors-blue-500)}",
11002
+ ".cw-readonlyFocused{background-color:var(--chakra-colors-gray-50)}",
11003
+ ".cw-readonlyFocused::before{border:2px solid var(--chakra-colors-gray-400)}",
11004
+ ".cw-selected{background-color:var(--chakra-colors-blue-100);user-select:none}",
11005
+ ".cw-anchor::before{border:2px solid var(--chakra-colors-blue-400)}"
11006
+ ].join("");
11007
+ document.head.appendChild(style);
11008
+ }
11009
+ const INNER_STYLE = {
11010
+ display: "flex",
11011
+ alignItems: "center",
11012
+ height: "100%",
11013
+ width: "100%",
11014
+ minWidth: 0,
11015
+ overflow: "hidden"
11016
+ };
11017
+ function buildSelectionBoxShadow(edges) {
11018
+ const shadows = [];
11019
+ if (edges.top) shadows.push(`inset 0 2px 0 0 ${SELECTION_BORDER_COLOR}`);
11020
+ if (edges.bottom) shadows.push(`inset 0 -2px 0 0 ${SELECTION_BORDER_COLOR}`);
11021
+ if (edges.left) shadows.push(`inset 2px 0 0 0 ${SELECTION_BORDER_COLOR}`);
11022
+ if (edges.right) shadows.push(`inset -2px 0 0 0 ${SELECTION_BORDER_COLOR}`);
11023
+ return shadows.length > 0 ? shadows.join(", ") : null;
11024
+ }
11025
+
11026
+ //#endregion
11027
+ //#region src/table-editor/Table/ui/Cell/useCellContextMenu.ts
11028
+ let pendingContextMenu = null;
11029
+ function clearPendingContextMenu() {
11030
+ const pending = pendingContextMenu;
11031
+ pendingContextMenu = null;
11032
+ return pending;
11033
+ }
11034
+ function setPendingContextMenu(value) {
11035
+ pendingContextMenu = value;
11036
+ }
11037
+ const cellMenuRegistry = /* @__PURE__ */ new WeakMap();
11038
+ function useCellContextMenu(cell, cellRef, deferredEdit) {
11039
+ const [menuAnchor, setMenuAnchor] = (0, react.useState)(null);
11040
+ const menuOpen = menuAnchor !== null;
11041
+ const openContextMenuAt = (0, react.useCallback)((clientX, clientY) => {
11042
+ if (!cell.isFocused && !cell.isInSelection) cell.focus();
11043
+ setMenuAnchor(new DOMRect(clientX, clientY, 0, 0));
11044
+ }, [cell]);
11045
+ (0, react.useEffect)(() => {
11046
+ const el = cellRef.current;
11047
+ if (el) cellMenuRegistry.set(el, openContextMenuAt);
11048
+ return () => {
11049
+ if (el) cellMenuRegistry.delete(el);
11050
+ };
11051
+ }, [cellRef, openContextMenuAt]);
11052
+ const menuCloseRef = (0, react.useRef)(null);
11053
+ const handleMenuClose = (0, react.useCallback)(() => {
11054
+ const pending = clearPendingContextMenu();
11055
+ const didTriggerEdit = deferredEdit.triggerIfRequested();
11056
+ if (!pending && !didTriggerEdit && !cell.isEditing) if (cell.isAnchor || cell.isFocused && !cell.hasRangeSelection) cellRef.current?.focus();
11057
+ else ((cellRef.current?.closest("[data-testid=\"table-widget\"]"))?.querySelector("[data-testid^=\"cell-\"][tabindex=\"0\"]"))?.focus();
11058
+ setMenuAnchor(null);
11059
+ if (pending) {
11060
+ const openFn = cellMenuRegistry.get(pending.target);
11061
+ if (openFn) setTimeout(() => {
11062
+ openFn(pending.clientX, pending.clientY);
11063
+ }, 0);
11064
+ }
11065
+ }, [
11066
+ cell,
11067
+ cellRef,
11068
+ deferredEdit
11069
+ ]);
11070
+ menuCloseRef.current = handleMenuClose;
11071
+ return {
11072
+ menuAnchor,
11073
+ menuOpen,
11074
+ openContextMenuAt,
11075
+ handleMenuClose,
11076
+ menuCloseRef
11077
+ };
11078
+ }
11079
+
11080
+ //#endregion
11081
+ //#region src/table-editor/Table/ui/Cell/useCellFocus.ts
11082
+ function useCellFocus(cellRef, state, isAnchorInRange, navVersion) {
11083
+ (0, react.useEffect)(() => {
11084
+ if (!cellRef.current) return;
11085
+ if (state === "focused" || state === "readonlyFocused" || isAnchorInRange) cellRef.current.focus();
11086
+ else if (state === "display" || state === "readonly" || state === "selected") cellRef.current.blur();
11087
+ }, [
11088
+ cellRef,
11089
+ state,
11090
+ isAnchorInRange,
11091
+ navVersion
11092
+ ]);
11093
+ }
11094
+
11095
+ //#endregion
11096
+ //#region src/table-editor/Table/ui/Cell/useCellKeyboard.ts
11097
+ function handleArrowKey(cell, e, shiftAction, moveAction) {
11098
+ e.preventDefault();
11099
+ if (e.shiftKey) shiftAction();
11100
+ else if (cell.hasRangeSelection) cell.focus();
11101
+ else moveAction();
11102
+ }
11103
+ function handleEditableKeys(cell, e, callbacks) {
11104
+ if (cell.isReadOnly) {
11105
+ if (e.key === "Enter" || e.key === "Delete" || e.key === "Backspace" || isPrintableKey(e)) {
11106
+ e.preventDefault();
11107
+ cell.notifyReadonlyEditAttempt();
11108
+ }
11109
+ return;
11110
+ }
11111
+ const hasRange = cell.hasRangeSelection;
11112
+ if (!hasRange && e.key === "Enter") {
11113
+ e.preventDefault();
11114
+ if (callbacks.onStartEdit) callbacks.onStartEdit();
11115
+ else if (callbacks.onDoubleClick) callbacks.onDoubleClick();
11116
+ } else if (!hasRange && (e.key === "Delete" || e.key === "Backspace") && callbacks.onDelete) {
11117
+ e.preventDefault();
11118
+ callbacks.onDelete();
11119
+ } else if (isPrintableKey(e) && callbacks.onTypeChar) {
11120
+ e.preventDefault();
11121
+ callbacks.onTypeChar(e.key);
11122
+ }
11123
+ }
11124
+ function useCellKeyboard(cell, state, isAnchorInRange, menuOpen, menuCloseRef, callbacks) {
11125
+ const { onStartEdit, onDoubleClick, onTypeChar, onDelete } = callbacks;
11126
+ return {
11127
+ handleKeyDown: (0, react.useCallback)((e) => {
11128
+ if (menuOpen && e.key === "Escape") {
11129
+ e.preventDefault();
11130
+ menuCloseRef.current?.();
11131
+ return;
11132
+ }
11133
+ if (!(state === "focused" || state === "readonlyFocused" || isAnchorInRange)) return;
11134
+ const isMod = e.ctrlKey || e.metaKey;
11135
+ if (isMod && e.key === "c" && !cell.hasRangeSelection) {
11136
+ e.preventDefault();
11137
+ cell.copyToClipboard();
11138
+ return;
11139
+ }
11140
+ if (isMod && e.key === "v") return;
11141
+ const arrow = {
11142
+ ArrowUp: [cell.shiftMoveUp, cell.moveUp],
11143
+ ArrowDown: [cell.shiftMoveDown, cell.moveDown],
11144
+ ArrowLeft: [cell.shiftMoveLeft, cell.moveLeft],
11145
+ ArrowRight: [cell.shiftMoveRight, cell.moveRight]
11146
+ }[e.key];
11147
+ if (e.key === "Escape") {
11148
+ e.preventDefault();
11149
+ if (cell.hasRangeSelection) cell.focus();
11150
+ else cell.blur();
11151
+ } else if (arrow) handleArrowKey(cell, e, arrow[0], arrow[1]);
11152
+ else if (e.key === "Tab") {
11153
+ e.preventDefault();
11154
+ cell.handleTab(e.shiftKey);
11155
+ } else handleEditableKeys(cell, e, {
11156
+ onStartEdit,
11157
+ onDoubleClick,
11158
+ onTypeChar,
11159
+ onDelete
11160
+ });
11161
+ }, [
11162
+ state,
11163
+ cell,
11164
+ isAnchorInRange,
11165
+ menuOpen,
11166
+ menuCloseRef,
11167
+ onStartEdit,
11168
+ onDoubleClick,
11169
+ onTypeChar,
11170
+ onDelete
11171
+ ]),
11172
+ handleDoubleClick: (0, react.useCallback)((e) => {
11173
+ if (state === "readonly" || state === "readonlyFocused") {
11174
+ cell.notifyReadonlyEditAttempt();
11175
+ return;
11176
+ }
11177
+ onDoubleClick?.(e.clientX);
11178
+ }, [
11179
+ state,
11180
+ cell,
11181
+ onDoubleClick
11182
+ ])
11183
+ };
11184
+ }
11185
+
10854
11186
  //#endregion
10855
11187
  //#region src/table-editor/Table/ui/Cell/useDeferredMenuEdit.ts
10856
11188
  /**
@@ -10905,299 +11237,117 @@ function useDeferredMenuEdit(editFn) {
10905
11237
  };
10906
11238
  }
10907
11239
 
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
11240
  //#endregion
11030
11241
  //#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
- }
11242
+ const LazyContextMenu = (0, mobx_react_lite.observer)(({ cell, cellRef, anchorRect, onClose, onEditPointerDown }) => {
11243
+ const handleOpenChange = (0, react.useCallback)((details) => {
11244
+ if (!details.open) onClose();
11245
+ }, [onClose]);
11246
+ const handleInteractOutside = (0, react.useCallback)((e) => {
11247
+ const originalEvent = e.detail?.originalEvent;
11248
+ if (!originalEvent || originalEvent.button !== 2) return;
11249
+ const targetCell = originalEvent.target?.closest("[data-testid^=\"cell-\"]");
11250
+ if (targetCell) setPendingContextMenu({
11251
+ target: targetCell,
11252
+ clientX: originalEvent.clientX,
11253
+ clientY: originalEvent.clientY
11254
+ });
11255
+ }, []);
11256
+ const getAnchorRect = (0, react.useCallback)(() => {
11257
+ return anchorRect ?? (cellRef.current?.getBoundingClientRect() || null);
11258
+ }, [anchorRect, cellRef]);
11259
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Menu.Root, {
11260
+ open: true,
11261
+ onOpenChange: handleOpenChange,
11262
+ onInteractOutside: handleInteractOutside,
11263
+ positioning: {
11264
+ placement: "bottom-start",
11265
+ getAnchorRect
11266
+ },
11267
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CellContextMenu, {
11268
+ cell,
11269
+ onEditPointerDown
11270
+ })
11271
+ });
11272
+ });
11055
11273
  const CellWrapper = (0, mobx_react_lite.observer)(({ cell, children, onDoubleClick, onStartEdit, onTypeChar, onDelete }) => {
11274
+ ensureCellStyles();
11056
11275
  const cellRef = (0, react.useRef)(null);
11057
- const menuOpenRef = (0, react.useRef)(false);
11058
- const deferredEdit = useDeferredMenuEdit(onStartEdit ?? onDoubleClick);
11059
11276
  const state = getCellState(cell);
11060
11277
  const selectionEdges = cell.selectionEdges;
11061
11278
  const isAnchorInRange = cell.isAnchor && cell.isInSelection;
11062
11279
  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]);
11280
+ const isActive = state === "focused" || state === "editing" || state === "readonlyFocused" || isAnchorInRange;
11281
+ const deferredEdit = useDeferredMenuEdit(onStartEdit ?? onDoubleClick);
11282
+ const { menuAnchor, menuOpen, openContextMenuAt, handleMenuClose, menuCloseRef } = useCellContextMenu(cell, cellRef, deferredEdit);
11283
+ useCellFocus(cellRef, state, isAnchorInRange, navVersion);
11284
+ const { handleKeyDown, handleDoubleClick } = useCellKeyboard(cell, state, isAnchorInRange, menuOpen, menuCloseRef, {
11285
+ onStartEdit,
11286
+ onDoubleClick,
11287
+ onTypeChar,
11288
+ onDelete
11289
+ });
11077
11290
  const handleMouseDown = (0, react.useCallback)((e) => {
11078
11291
  if (e.detail === 2 && state !== "readonly" && state !== "readonlyFocused") e.preventDefault();
11292
+ if (e.button === 2) {
11293
+ openContextMenuAt(e.clientX, e.clientY);
11294
+ return;
11295
+ }
11079
11296
  if (!e.shiftKey && e.button === 0 && state !== "editing") {
11080
11297
  e.preventDefault();
11081
11298
  cell.dragStart();
11082
11299
  cellRef.current?.focus();
11083
11300
  }
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
- }, [
11095
- state,
11096
- cell,
11097
- onDoubleClick
11098
- ]);
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
11301
  }, [
11121
11302
  state,
11122
11303
  cell,
11123
- isAnchorInRange,
11124
- onStartEdit,
11125
- onDoubleClick,
11126
- onTypeChar,
11127
- onDelete
11304
+ openContextMenuAt
11128
11305
  ]);
11129
- const selectionShadow = selectionEdges ? buildSelectionBoxShadow(selectionEdges) : null;
11130
- const needsFocus = state === "focused" || state === "editing" || state === "readonlyFocused" || isAnchorInRange;
11306
+ const handleClick = (0, react.useCallback)((e) => {
11307
+ if (state === "editing") return;
11308
+ if (e.shiftKey) cell.selectTo();
11309
+ else cell.focus();
11310
+ }, [state, cell]);
11311
+ const handleMouseEnter = (0, react.useCallback)((e) => {
11312
+ if (e.buttons === 1) cell.dragExtend();
11313
+ }, [cell]);
11131
11314
  const handleBlur = (0, react.useCallback)((e) => {
11132
11315
  if (!cell.isFocused || cell.isEditing) return;
11133
- if (menuOpenRef.current) return;
11316
+ if (menuOpen) return;
11134
11317
  if (e.relatedTarget?.closest("[data-testid^=\"cell-\"]")) return;
11135
11318
  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
- });
11319
+ }, [cell, menuOpen]);
11320
+ const selectionShadow = selectionEdges ? buildSelectionBoxShadow(selectionEdges) : null;
11321
+ let className = STATE_CLASS[state];
11322
+ if (isAnchorInRange) className += ` ${CLS_ANCHOR}`;
11323
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
11324
+ ref: cellRef,
11325
+ className,
11326
+ style: selectionShadow ? { "--cw-shadow": selectionShadow } : void 0,
11327
+ tabIndex: isActive ? 0 : -1,
11328
+ onClick: handleClick,
11329
+ onMouseDown: handleMouseDown,
11330
+ onMouseEnter: handleMouseEnter,
11331
+ onDoubleClick: isActive ? handleDoubleClick : void 0,
11332
+ onKeyDown: isActive ? handleKeyDown : void 0,
11333
+ onBlur: isActive ? handleBlur : void 0,
11334
+ onContextMenu: preventContextMenu,
11335
+ "data-testid": `cell-${cell.rowId}-${cell.field}`,
11336
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
11337
+ style: INNER_STYLE,
11338
+ children
11339
+ })
11340
+ }), menuOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LazyContextMenu, {
11341
+ cell,
11342
+ cellRef,
11343
+ anchorRect: menuAnchor,
11344
+ onClose: handleMenuClose,
11345
+ onEditPointerDown: deferredEdit.requestEdit
11346
+ })] });
11200
11347
  });
11348
+ function preventContextMenu(e) {
11349
+ e.preventDefault();
11350
+ }
11201
11351
 
11202
11352
  //#endregion
11203
11353
  //#region src/table-editor/Table/ui/Cell/useTextareaCell.ts
@@ -11830,6 +11980,30 @@ function clearRange(range, rows, cols) {
11830
11980
  }
11831
11981
  }
11832
11982
 
11983
+ //#endregion
11984
+ //#region src/table-editor/Table/ui/borderConstants.ts
11985
+ const CELL_BORDER_COLOR = "#ededed";
11986
+ const BOTTOM_BORDER_SHADOW = `inset 0 -1px 0 0 ${CELL_BORDER_COLOR}`;
11987
+ function buildAddColumnShadowCss() {
11988
+ return { "&::after": {
11989
+ content: "\"\"",
11990
+ position: "absolute",
11991
+ top: 0,
11992
+ bottom: 0,
11993
+ width: "8px",
11994
+ left: "-8px",
11995
+ pointerEvents: "none",
11996
+ transition: "opacity 0.15s",
11997
+ opacity: "var(--shadow-right-opacity, 0)",
11998
+ boxShadow: "inset -8px 0 12px -8px rgba(0,0,0,0.1)"
11999
+ } };
12000
+ }
12001
+ function adjustRightOffsetCss(rightCss, addColOffset) {
12002
+ if (addColOffset <= 0) return rightCss;
12003
+ if (rightCss === "0px") return `${addColOffset}px`;
12004
+ return `calc(${rightCss} + ${addColOffset}px)`;
12005
+ }
12006
+
11833
12007
  //#endregion
11834
12008
  //#region src/table-editor/Table/ui/ResizeHandle.tsx
11835
12009
  const MIN_COLUMN_WIDTH = 40;
@@ -12107,8 +12281,8 @@ const InsertColumnSubmenu = ({ label, valuePrefix, availableFields, onSelect })
12107
12281
  //#region src/table-editor/Table/ui/Header/ColumnHeaderMenu.tsx
12108
12282
  const ColumnHeaderMenu = (0, mobx_react_lite.observer)(({ column, columnsModel, sortModel, filterModel, onCopyPath, onClose }) => {
12109
12283
  const canRemove = columnsModel.canRemoveColumn;
12110
- const availableFields = columnsModel.availableFieldsToAdd;
12111
- const hasAvailableFields = availableFields.length > 0;
12284
+ const insertableFields = columnsModel.availableFieldsToInsert;
12285
+ const hasInsertableFields = insertableFields.length > 0;
12112
12286
  const canMove = columnsModel.canMoveLeft(column.field) || columnsModel.canMoveRight(column.field);
12113
12287
  const isPinned = columnsModel.isPinned(column.field);
12114
12288
  const canPinLeft = columnsModel.canPinLeft(column.field);
@@ -12146,11 +12320,11 @@ const ColumnHeaderMenu = (0, mobx_react_lite.observer)(({ column, columnsModel,
12146
12320
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Menu.Content, {
12147
12321
  minW: "180px",
12148
12322
  children: [
12149
- sortModel && !column.hasFormula && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SortSubmenu, {
12323
+ sortModel && column.isSortable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SortSubmenu, {
12150
12324
  field: column.field,
12151
12325
  sortModel
12152
12326
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Menu.Separator, {})] }),
12153
- filterModel && !column.hasFormula && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Menu.Item, {
12327
+ filterModel && column.isSortable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Menu.Item, {
12154
12328
  value: "add-filter",
12155
12329
  onClick: handleAddFilter,
12156
12330
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_icons_lu.LuFilter, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Text, { children: "Add filter" })]
@@ -12176,17 +12350,17 @@ const ColumnHeaderMenu = (0, mobx_react_lite.observer)(({ column, columnsModel,
12176
12350
  }),
12177
12351
  (canPinLeft || canPinRight) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Menu.Separator, {})
12178
12352
  ] }),
12179
- hasAvailableFields && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
12353
+ hasInsertableFields && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
12180
12354
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InsertColumnSubmenu, {
12181
12355
  label: "Insert before",
12182
12356
  valuePrefix: "before",
12183
- availableFields,
12357
+ availableFields: insertableFields,
12184
12358
  onSelect: handleInsertBefore
12185
12359
  }),
12186
12360
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InsertColumnSubmenu, {
12187
12361
  label: "Insert after",
12188
12362
  valuePrefix: "after",
12189
- availableFields,
12363
+ availableFields: insertableFields,
12190
12364
  onSelect: handleInsertAfter
12191
12365
  }),
12192
12366
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Menu.Separator, {})
@@ -12304,11 +12478,11 @@ const ColumnHeader = (0, mobx_react_lite.observer)(({ column, columnsModel, sort
12304
12478
  field: column.field,
12305
12479
  columnsModel
12306
12480
  }),
12307
- filterModel && !column.hasFormula && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterIndicator, {
12481
+ filterModel && column.isSortable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterIndicator, {
12308
12482
  field: column.field,
12309
12483
  filterModel
12310
12484
  }),
12311
- sortModel && !column.hasFormula && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SortIndicator, {
12485
+ sortModel && column.isSortable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SortIndicator, {
12312
12486
  field: column.field,
12313
12487
  sortModel
12314
12488
  })
@@ -13018,12 +13192,20 @@ const TableWidget = (0, mobx_react_lite.observer)(({ rows, columnsModel, cellFSM
13018
13192
  if (isResizing) shadowModel.pause();
13019
13193
  else shadowModel.resume();
13020
13194
  }, [isResizing, shadowModel]);
13195
+ const [wrapperEl, setWrapperEl] = (0, react.useState)(null);
13021
13196
  const wrapperRefCallback = (0, react.useCallback)((el) => {
13197
+ setWrapperEl(el);
13022
13198
  columnsModel.setWrapperElement(el);
13023
- if (useWindowScrollProp) setScrollerRef(el);
13199
+ }, [columnsModel]);
13200
+ (0, react.useEffect)(() => {
13201
+ if (!useWindowScrollProp || !wrapperEl) return;
13202
+ setScrollerRef(wrapperEl);
13203
+ return () => {
13204
+ setScrollerRef(null);
13205
+ };
13024
13206
  }, [
13025
- columnsModel,
13026
13207
  useWindowScrollProp,
13208
+ wrapperEl,
13027
13209
  setScrollerRef
13028
13210
  ]);
13029
13211
  const handleSelectRow = (0, react.useCallback)((rowId) => {
@@ -13416,67 +13598,6 @@ var ViewSettingsBadgeModel = class {
13416
13598
  }
13417
13599
  };
13418
13600
 
13419
- //#endregion
13420
- //#region src/table-editor/TableEditor/model/SchemaContext.ts
13421
- const SYSTEM_REF_SCHEMAS = {
13422
- [_revisium_schema_toolkit.SystemSchemaIds.File]: _revisium_schema_toolkit.fileSchema,
13423
- [_revisium_schema_toolkit.SystemSchemaIds.RowId]: _revisium_schema_toolkit.rowIdSchema,
13424
- [_revisium_schema_toolkit.SystemSchemaIds.RowCreatedAt]: _revisium_schema_toolkit.rowCreatedAtSchema,
13425
- [_revisium_schema_toolkit.SystemSchemaIds.RowCreatedId]: _revisium_schema_toolkit.rowCreatedIdSchema,
13426
- [_revisium_schema_toolkit.SystemSchemaIds.RowVersionId]: _revisium_schema_toolkit.rowVersionIdSchema,
13427
- [_revisium_schema_toolkit.SystemSchemaIds.RowPublishedAt]: _revisium_schema_toolkit.rowPublishedAtSchema,
13428
- [_revisium_schema_toolkit.SystemSchemaIds.RowUpdatedAt]: _revisium_schema_toolkit.rowUpdatedAtSchema,
13429
- [_revisium_schema_toolkit.SystemSchemaIds.RowHash]: _revisium_schema_toolkit.rowHashSchema,
13430
- [_revisium_schema_toolkit.SystemSchemaIds.RowSchemaHash]: _revisium_schema_toolkit.rowSchemaHashSchema
13431
- };
13432
- function buildRowSchema(dataSchema) {
13433
- const systemProperties = {};
13434
- for (const sf of SYSTEM_FIELDS) systemProperties[sf.id] = { $ref: sf.ref };
13435
- return {
13436
- type: "object",
13437
- properties: {
13438
- ...systemProperties,
13439
- ...dataSchema.properties
13440
- },
13441
- additionalProperties: false,
13442
- required: [...Object.keys(systemProperties), ...dataSchema.required ?? []]
13443
- };
13444
- }
13445
- var SchemaContext = class {
13446
- _allColumns = [];
13447
- _dataSchema = null;
13448
- _fullRefSchemas = {};
13449
- _rootNode = null;
13450
- get allColumns() {
13451
- return this._allColumns;
13452
- }
13453
- get sortableFields() {
13454
- return this._allColumns.filter((col) => !col.isDeprecated && col.fieldType !== FilterFieldType.File);
13455
- }
13456
- get filterableFields() {
13457
- return this.sortableFields;
13458
- }
13459
- get dataSchema() {
13460
- return this._dataSchema;
13461
- }
13462
- get fullRefSchemas() {
13463
- return this._fullRefSchemas;
13464
- }
13465
- get rootNode() {
13466
- return this._rootNode;
13467
- }
13468
- init(dataSchema, refSchemas) {
13469
- this._dataSchema = dataSchema;
13470
- this._fullRefSchemas = {
13471
- ...SYSTEM_REF_SCHEMAS,
13472
- ...refSchemas
13473
- };
13474
- const rowSchema = buildRowSchema(dataSchema);
13475
- this._rootNode = new _revisium_schema_toolkit.SchemaParser().parse(rowSchema, this._fullRefSchemas);
13476
- this._allColumns = extractColumns(this._rootNode);
13477
- }
13478
- };
13479
-
13480
13601
  //#endregion
13481
13602
  //#region src/table-editor/TableEditor/model/TableEditorCore.ts
13482
13603
  const DEFAULT_PAGE_SIZE = 50;
@@ -13623,7 +13744,10 @@ var TableEditorCore = class {
13623
13744
  _buildQuery(after = null) {
13624
13745
  return {
13625
13746
  where: this.filters.hasActiveFilters ? this.filters.buildCurrentWhereClause() : null,
13626
- orderBy: this.sorts.serializeToViewSorts(),
13747
+ orderBy: this.sorts.serializeToViewSorts().map((s) => ({
13748
+ field: stripDataFieldPrefix(s.field),
13749
+ direction: s.direction
13750
+ })),
13627
13751
  search: this.search.debouncedQuery,
13628
13752
  first: this._pageSize,
13629
13753
  after
@@ -13658,14 +13782,14 @@ var TableEditorCore = class {
13658
13782
  this._updateNavigationContext();
13659
13783
  }
13660
13784
  _createRowVMs(rawRows) {
13661
- const dataSchema = this._schemaContext.dataSchema;
13662
- if (!dataSchema) return [];
13785
+ const wrappedSchema = this._schemaContext.wrappedDataSchema;
13786
+ if (!wrappedSchema) return [];
13663
13787
  const tableModel = (0, _revisium_schema_toolkit.createTableModel)({
13664
13788
  tableId: this._tableId,
13665
- schema: dataSchema,
13789
+ schema: wrappedSchema,
13666
13790
  rows: rawRows.map((r) => ({
13667
13791
  rowId: r.rowId,
13668
- data: r.data
13792
+ data: { data: r.data }
13669
13793
  })),
13670
13794
  refSchemas: this._schemaContext.fullRefSchemas
13671
13795
  });
@@ -13679,10 +13803,14 @@ var TableEditorCore = class {
13679
13803
  id: rawRow.rowId
13680
13804
  };
13681
13805
  }
13806
+ _toPatchField(field) {
13807
+ return stripDataFieldPrefix(field);
13808
+ }
13682
13809
  async _commitCell(rowId, field, value, previousValue) {
13810
+ const patchField = this._toPatchField(field);
13683
13811
  const result = (await this._dataSource.patchCells([{
13684
13812
  rowId,
13685
- field,
13813
+ field: patchField,
13686
13814
  value
13687
13815
  }]))[0];
13688
13816
  if (result && !result.ok) (0, mobx.runInAction)(() => {
@@ -13762,7 +13890,7 @@ var MockDataSource = class {
13762
13890
  static createRow(rowId, data, systemFields) {
13763
13891
  const row = {
13764
13892
  rowId,
13765
- data: { ...data }
13893
+ data: data && typeof data === "object" && !Array.isArray(data) ? { ...data } : data
13766
13894
  };
13767
13895
  if (systemFields) row.systemFields = systemFields;
13768
13896
  return row;
@@ -13781,16 +13909,23 @@ var MockDataSource = class {
13781
13909
  let rows = [...this._allRows];
13782
13910
  if (query.search) {
13783
13911
  const lower = query.search.toLowerCase();
13784
- rows = rows.filter((row) => Object.values(row.data).some((val) => typeof val === "string" && val.toLowerCase().includes(lower)));
13912
+ rows = rows.filter((row) => {
13913
+ const data = row.data;
13914
+ if (data && typeof data === "object") return Object.values(data).some((val) => typeof val === "string" && val.toLowerCase().includes(lower));
13915
+ if (typeof data === "string") return data.toLowerCase().includes(lower);
13916
+ return false;
13917
+ });
13785
13918
  }
13786
13919
  if (query.orderBy.length > 0) {
13787
13920
  const firstOrder = query.orderBy[0];
13788
13921
  if (firstOrder) {
13789
- const field = firstOrder.field.startsWith("data.") ? firstOrder.field.slice(5) : firstOrder.field;
13922
+ const field = firstOrder.field;
13790
13923
  const dir = firstOrder.direction === "desc" ? -1 : 1;
13791
13924
  rows.sort((a, b) => {
13792
- const aVal = a.data[field];
13793
- const bVal = b.data[field];
13925
+ const aData = a.data;
13926
+ const bData = b.data;
13927
+ const aVal = aData?.[field];
13928
+ const bVal = bData?.[field];
13794
13929
  if (aVal === bVal) return 0;
13795
13930
  if (aVal == null) return 1;
13796
13931
  if (bVal == null) return -1;
@@ -13826,7 +13961,7 @@ var MockDataSource = class {
13826
13961
  error: "Mock failure"
13827
13962
  };
13828
13963
  const row = this._allRows.find((r) => r.rowId === patch.rowId);
13829
- if (row) row.data[patch.field] = patch.value;
13964
+ if (row?.data && typeof row.data === "object" && !Array.isArray(row.data)) row.data[patch.field] = patch.value;
13830
13965
  return {
13831
13966
  rowId: patch.rowId,
13832
13967
  field: patch.field,