@revisium/schema-toolkit-ui 0.4.0 → 0.5.0

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
@@ -6593,7 +6593,12 @@ const FileActions = (0, mobx_react_lite.observer)(({ node }) => {
6593
6593
  const file = event.target.files?.[0];
6594
6594
  if (!file) return;
6595
6595
  event.target.value = "";
6596
- const result = await node.callbacks?.onUploadFile?.(fileId, file);
6596
+ const rowId = node.editorContext?.rowId ?? "";
6597
+ const result = await node.callbacks?.onUploadFile?.({
6598
+ rowId,
6599
+ fileId,
6600
+ file
6601
+ });
6597
6602
  if (result) node.node.setValue(result, { internal: true });
6598
6603
  }, [node, fileId]);
6599
6604
  const handleOpenFile = (0, react.useCallback)(() => {
@@ -6930,9 +6935,10 @@ function extractColumns(rootNode) {
6930
6935
  function collectColumns(node, parentPath, columns) {
6931
6936
  for (const child of node.properties()) {
6932
6937
  const fieldPath = buildFieldPath(parentPath, child.name());
6933
- const column = resolveColumn(child, fieldPath);
6934
- if (column === "recurse") collectColumns(child, fieldPath, columns);
6935
- else if (column) columns.push(column);
6938
+ const result = resolveColumn(child, fieldPath);
6939
+ if (result === "recurse") collectColumns(child, fieldPath, columns);
6940
+ else if (result) if (Array.isArray(result)) for (const col of result) columns.push(col);
6941
+ else columns.push(result);
6936
6942
  }
6937
6943
  }
6938
6944
  function buildFieldPath(parentPath, fieldName) {
@@ -6940,8 +6946,8 @@ function buildFieldPath(parentPath, fieldName) {
6940
6946
  }
6941
6947
  function resolveColumn(child, fieldPath) {
6942
6948
  if (child.isArray()) return null;
6943
- const refColumn = resolveRefColumn(child, fieldPath);
6944
- if (refColumn) return refColumn;
6949
+ const refResult = resolveRefColumn(child, fieldPath);
6950
+ if (refResult) return refResult;
6945
6951
  if (child.isObject() && !child.isRef()) return "recurse";
6946
6952
  if (child.foreignKey() !== void 0) {
6947
6953
  const col = createColumn(fieldPath, child, FilterFieldType.ForeignKey);
@@ -6965,13 +6971,22 @@ function resolveRefColumn(child, fieldPath) {
6965
6971
  isDeprecated: child.metadata().deprecated ?? false,
6966
6972
  hasFormula: child.hasFormula()
6967
6973
  };
6968
- if (refValue === _revisium_schema_toolkit.SystemSchemaIds.File) return createColumn(fieldPath, child, FilterFieldType.File);
6974
+ if (refValue === _revisium_schema_toolkit.SystemSchemaIds.File) return resolveFileRefColumns(child, fieldPath);
6969
6975
  return null;
6970
6976
  }
6977
+ function resolveFileRefColumns(child, fieldPath) {
6978
+ const result = [createColumn(fieldPath, child, FilterFieldType.File)];
6979
+ if (child.isObject()) for (const subField of child.properties()) {
6980
+ const subFieldPath = buildFieldPath(fieldPath, subField.name());
6981
+ const fieldType = NODE_TYPE_TO_FIELD_TYPE[subField.nodeType()];
6982
+ if (fieldType) result.push(createColumn(subFieldPath, subField, fieldType));
6983
+ }
6984
+ return result;
6985
+ }
6971
6986
  function createColumn(fieldPath, child, fieldType) {
6972
6987
  return {
6973
6988
  field: fieldPath,
6974
- label: child.name(),
6989
+ label: fieldPath,
6975
6990
  fieldType,
6976
6991
  isSystem: false,
6977
6992
  isDeprecated: child.metadata().deprecated ?? false,
@@ -6981,9 +6996,9 @@ function createColumn(fieldPath, child, fieldType) {
6981
6996
 
6982
6997
  //#endregion
6983
6998
  //#region src/table-editor/Columns/model/selectDefaultColumns.ts
6984
- const SEMANTIC_NAME_PATTERNS = /^(title|name|label)$/i;
6999
+ const SEMANTIC_NAME_PATTERNS = /^(title|name|label|subject|summary|description|heading|caption|email|username|displayName|firstName|lastName)$/i;
6985
7000
  const PRIORITY_BY_TYPE = {
6986
- [FilterFieldType.File]: 0,
7001
+ [FilterFieldType.File]: 2,
6987
7002
  [FilterFieldType.String]: 2,
6988
7003
  [FilterFieldType.Number]: 3,
6989
7004
  [FilterFieldType.Boolean]: 3,
@@ -6994,8 +7009,23 @@ function columnPriority(col) {
6994
7009
  if (col.fieldType === FilterFieldType.String && SEMANTIC_NAME_PATTERNS.test(col.label)) return 1;
6995
7010
  return PRIORITY_BY_TYPE[col.fieldType];
6996
7011
  }
6997
- function selectDefaultColumns(columns, maxVisible = 3) {
6998
- return [...columns.filter((col) => !col.isSystem && !col.isDeprecated)].sort((a, b) => columnPriority(a) - columnPriority(b)).slice(0, maxVisible);
7012
+ function selectDefaultColumns(columns, maxVisible = 4) {
7013
+ const idColumn = columns.find((col) => col.isSystem && col.field === "id");
7014
+ const fileFieldPrefixes = columns.filter((col) => col.fieldType === FilterFieldType.File).map((col) => `${col.field}.`);
7015
+ const sorted = [...columns.filter((col) => !col.isSystem && !col.isDeprecated && !fileFieldPrefixes.some((prefix) => col.field.startsWith(prefix)))].sort((a, b) => columnPriority(a) - columnPriority(b));
7016
+ const dataSlots = idColumn ? maxVisible - 1 : maxVisible;
7017
+ let fileIncluded = false;
7018
+ const dataColumns = [];
7019
+ for (const col of sorted) {
7020
+ if (dataColumns.length >= dataSlots) break;
7021
+ if (col.fieldType === FilterFieldType.File) {
7022
+ if (fileIncluded) continue;
7023
+ fileIncluded = true;
7024
+ }
7025
+ dataColumns.push(col);
7026
+ }
7027
+ if (idColumn) return [idColumn, ...dataColumns];
7028
+ return dataColumns;
6999
7029
  }
7000
7030
 
7001
7031
  //#endregion
@@ -7038,10 +7068,13 @@ var ColumnsModel = class {
7038
7068
  return this._visibleFields.length > 1;
7039
7069
  }
7040
7070
  get sortableFields() {
7041
- return this._allColumns.filter((col) => !col.isDeprecated);
7071
+ return this._operableFields;
7042
7072
  }
7043
7073
  get filterableFields() {
7044
- return this._allColumns.filter((col) => !col.isDeprecated);
7074
+ return this._operableFields;
7075
+ }
7076
+ get _operableFields() {
7077
+ return this._allColumns.filter((col) => !col.isDeprecated && col.fieldType !== FilterFieldType.File);
7045
7078
  }
7046
7079
  get pinnedLeftCount() {
7047
7080
  let count = 0;
@@ -7164,6 +7197,8 @@ var ColumnsModel = class {
7164
7197
  }
7165
7198
  setColumnWidth(field, width) {
7166
7199
  this._columnWidths.set(field, width);
7200
+ }
7201
+ commitColumnWidth() {
7167
7202
  this._notifyChange();
7168
7203
  }
7169
7204
  getColumnWidth(field) {
@@ -7290,7 +7325,8 @@ var ColumnsModel = class {
7290
7325
  if (vc.pinned !== void 0) this._pinnedColumns.set(field, vc.pinned);
7291
7326
  }
7292
7327
  }
7293
- this._visibleFields = fields;
7328
+ if (fields.length === 0) this._visibleFields = selectDefaultColumns(this._allColumns).map((col) => col.field);
7329
+ else this._visibleFields = fields;
7294
7330
  }
7295
7331
  setOnChange(cb) {
7296
7332
  this._onChange = cb;
@@ -8146,9 +8182,25 @@ var FilterCore = class FilterCore {
8146
8182
  this._notifyChange();
8147
8183
  }
8148
8184
  applySnapshot(serialized) {
8149
- this._committedHasFilters = this._serializer.applySnapshot(serialized);
8185
+ let parsed;
8186
+ try {
8187
+ parsed = JSON.parse(serialized);
8188
+ } catch {
8189
+ return;
8190
+ }
8191
+ if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.conditions) || !Array.isArray(parsed.groups)) return;
8192
+ const validFields = new Set(this._availableFields.map((f) => f.field));
8193
+ const cleaned = FilterCore._stripInvalidConditions(parsed, validFields);
8194
+ this._committedHasFilters = this._serializer.applySnapshot(JSON.stringify(cleaned));
8150
8195
  this._notifyChange();
8151
8196
  }
8197
+ static _stripInvalidConditions(group, validFields) {
8198
+ return {
8199
+ ...group,
8200
+ conditions: group.conditions.filter((c) => validFields.has(c.field)),
8201
+ groups: group.groups.map((g) => FilterCore._stripInvalidConditions(g, validFields)).filter((g) => g.conditions.length > 0 || g.groups.length > 0)
8202
+ };
8203
+ }
8152
8204
  clearAll() {
8153
8205
  this._row.reset(FilterCore._emptyFilterData());
8154
8206
  this._committedHasFilters = false;
@@ -9642,11 +9694,11 @@ var ObservableFSM = class {
9642
9694
  _onUnhandledEvent;
9643
9695
  constructor(config) {
9644
9696
  this._state = config.initial;
9645
- this._context = { ...config.context };
9697
+ this._context = mobx.observable.object({ ...config.context }, void 0, { deep: false });
9646
9698
  this._transitions = config.transitions;
9647
9699
  this._onTransition = config.onTransition;
9648
9700
  this._onUnhandledEvent = config.onUnhandledEvent;
9649
- (0, mobx.makeAutoObservable)(this, {}, { autoBind: true });
9701
+ (0, mobx.makeAutoObservable)(this, { _context: false }, { autoBind: true });
9650
9702
  }
9651
9703
  get state() {
9652
9704
  return this._state;
@@ -9932,6 +9984,7 @@ var CellFSM = class {
9932
9984
  _fsm;
9933
9985
  constructor() {
9934
9986
  this._fsm = new ObservableFSM(createConfig());
9987
+ (0, mobx.makeAutoObservable)(this, {}, { autoBind: true });
9935
9988
  }
9936
9989
  get state() {
9937
9990
  return this._fsm.state;
@@ -10116,13 +10169,15 @@ var CellVM = class {
10116
10169
  _rowId;
10117
10170
  _cellFSM;
10118
10171
  _onCommit;
10119
- constructor(rowModel, column, rowId, cellFSM, onCommit) {
10172
+ _systemValues;
10173
+ constructor(rowModel, column, rowId, cellFSM, onCommit, systemValues) {
10120
10174
  this._rowModel = rowModel;
10121
10175
  this._column = column;
10122
10176
  this._rowId = rowId;
10123
10177
  this._cellFSM = cellFSM;
10124
10178
  this._onCommit = onCommit ?? null;
10125
- (0, mobx.makeAutoObservable)(this, {}, { autoBind: true });
10179
+ this._systemValues = systemValues ?? null;
10180
+ (0, mobx.makeAutoObservable)(this, { selectionEdges: (0, mobx.computed)({ equals: mobx.comparer.structural }) }, { autoBind: true });
10126
10181
  }
10127
10182
  get field() {
10128
10183
  return this._column.field;
@@ -10137,6 +10192,7 @@ var CellVM = class {
10137
10192
  return this._rowId;
10138
10193
  }
10139
10194
  get value() {
10195
+ if (this._systemValues) return this._systemValues[this._column.field];
10140
10196
  const node = this._getNode();
10141
10197
  if (!node) return;
10142
10198
  return node.getPlainValue();
@@ -10148,10 +10204,21 @@ var CellVM = class {
10148
10204
  if (typeof val === "number") return String(val);
10149
10205
  if (typeof val === "string") return val;
10150
10206
  if (Array.isArray(val)) return `[${val.length}]`;
10151
- if (typeof val === "object") return "{...}";
10207
+ if (typeof val === "object") {
10208
+ const obj = val;
10209
+ if (typeof obj.fileName === "string" && obj.fileName) return obj.fileName;
10210
+ if (this._column.fieldType === FilterFieldType.File) return "";
10211
+ return "{...}";
10212
+ }
10152
10213
  return "";
10153
10214
  }
10154
10215
  get isReadOnly() {
10216
+ if (this._systemValues) return true;
10217
+ if (this._column.fieldType === FilterFieldType.File) {
10218
+ const fileNameNode = this._getFileNameNode();
10219
+ if (!fileNameNode?.isPrimitive()) return true;
10220
+ return fileNameNode.isReadOnly;
10221
+ }
10155
10222
  const node = this._getNode();
10156
10223
  if (!node) return true;
10157
10224
  if (node.isPrimitive()) return node.isReadOnly;
@@ -10160,8 +10227,25 @@ var CellVM = class {
10160
10227
  get foreignKeyTableId() {
10161
10228
  return this._column.foreignKeyTableId;
10162
10229
  }
10230
+ get fileData() {
10231
+ if (this._column.fieldType !== FilterFieldType.File) return null;
10232
+ const val = this.value;
10233
+ if (!val || typeof val !== "object" || Array.isArray(val)) return null;
10234
+ const obj = val;
10235
+ return {
10236
+ status: obj.status ?? "",
10237
+ fileId: obj.fileId ?? "",
10238
+ url: obj.url ?? "",
10239
+ fileName: obj.fileName ?? "",
10240
+ mimeType: obj.mimeType ?? "",
10241
+ width: typeof obj.width === "number" ? obj.width : 0,
10242
+ height: typeof obj.height === "number" ? obj.height : 0
10243
+ };
10244
+ }
10163
10245
  get isEditable() {
10246
+ if (this._systemValues) return false;
10164
10247
  if (this.isReadOnly) return false;
10248
+ if (this._column.fieldType === FilterFieldType.File) return this._getFileNameNode() !== void 0;
10165
10249
  const node = this._getNode();
10166
10250
  if (!node) return false;
10167
10251
  return node.isPrimitive();
@@ -10213,6 +10297,11 @@ var CellVM = class {
10213
10297
  this._cellFSM.doubleClick(clickOffset);
10214
10298
  }
10215
10299
  commitEdit(newValue) {
10300
+ if (this._column.fieldType === FilterFieldType.File) {
10301
+ this._commitFileNameEdit(newValue);
10302
+ this._cellFSM.commit();
10303
+ return;
10304
+ }
10216
10305
  const node = this._getNode();
10217
10306
  const previousValue = node?.getPlainValue();
10218
10307
  if (node?.isPrimitive()) node.setValue(newValue);
@@ -10220,6 +10309,11 @@ var CellVM = class {
10220
10309
  this._onCommit?.(this._rowId, this._column.field, newValue, previousValue);
10221
10310
  }
10222
10311
  commitEditAndMoveDown(newValue) {
10312
+ if (this._column.fieldType === FilterFieldType.File && newValue !== void 0) {
10313
+ this._commitFileNameEdit(newValue);
10314
+ this._cellFSM.commitAndMoveDown();
10315
+ return;
10316
+ }
10223
10317
  let previousValue;
10224
10318
  if (newValue !== void 0) {
10225
10319
  const node = this._getNode();
@@ -10232,6 +10326,13 @@ var CellVM = class {
10232
10326
  cancelEdit() {
10233
10327
  this._cellFSM.cancel();
10234
10328
  }
10329
+ commitFileUpload(result) {
10330
+ const node = this._getNode();
10331
+ if (!node?.isObject()) return;
10332
+ const previousValue = node.getPlainValue();
10333
+ node.setValue(result, { internal: true });
10334
+ this._onCommit?.(this._rowId, this._column.field, result, previousValue);
10335
+ }
10235
10336
  clearToDefault() {
10236
10337
  const node = this._getNode();
10237
10338
  if (!node || !node.isPrimitive() || node.isReadOnly) return;
@@ -10314,6 +10415,19 @@ var CellVM = class {
10314
10415
  _getNode() {
10315
10416
  return this._rowModel.get(this._column.field);
10316
10417
  }
10418
+ _getFileNameNode() {
10419
+ return this._rowModel.get(`${this._column.field}.fileName`);
10420
+ }
10421
+ _commitFileNameEdit(newFileName) {
10422
+ const fileNameNode = this._getFileNameNode();
10423
+ if (!fileNameNode?.isPrimitive()) return;
10424
+ const objectNode = this._getNode();
10425
+ if (!objectNode) return;
10426
+ const previousValue = objectNode.getPlainValue();
10427
+ fileNameNode.setValue(newFileName);
10428
+ const newValue = objectNode.getPlainValue();
10429
+ this._onCommit?.(this._rowId, this._column.field, newValue, previousValue);
10430
+ }
10317
10431
  _applyPastedString(node, text) {
10318
10432
  let trimmed = text;
10319
10433
  while (trimmed.endsWith("\n")) trimmed = trimmed.slice(0, -1);
@@ -10340,13 +10454,15 @@ var RowVM = class {
10340
10454
  _cellFSM;
10341
10455
  _selection;
10342
10456
  _onCellCommit;
10457
+ _systemValues;
10343
10458
  _cellCache = /* @__PURE__ */ new Map();
10344
- constructor(rowModel, rowId, cellFSM, selection, onCellCommit) {
10459
+ constructor(rowModel, rowId, cellFSM, selection, onCellCommit, systemValues) {
10345
10460
  this._rowModel = rowModel;
10346
10461
  this._rowId = rowId;
10347
10462
  this._cellFSM = cellFSM;
10348
10463
  this._selection = selection;
10349
10464
  this._onCellCommit = onCellCommit ?? null;
10465
+ this._systemValues = systemValues ?? {};
10350
10466
  (0, mobx.makeAutoObservable)(this, {}, { autoBind: true });
10351
10467
  }
10352
10468
  get rowId() {
@@ -10364,7 +10480,7 @@ var RowVM = class {
10364
10480
  getCellVM(column) {
10365
10481
  const cached = this._cellCache.get(column.field);
10366
10482
  if (cached) return cached;
10367
- const cell = new CellVM(this._rowModel, column, this._rowId, this._cellFSM, this._onCellCommit ?? void 0);
10483
+ const cell = new CellVM(this._rowModel, column, this._rowId, this._cellFSM, this._onCellCommit ?? void 0, column.isSystem ? this._systemValues : void 0);
10368
10484
  this._cellCache.set(column.field, cell);
10369
10485
  return cell;
10370
10486
  }
@@ -10758,14 +10874,13 @@ const stateStyles = {
10758
10874
  }
10759
10875
  };
10760
10876
  function getCellState(cell) {
10761
- const hasRange = cell.hasRangeSelection;
10762
10877
  if (cell.isReadOnly) {
10763
- if (cell.isFocused && !hasRange) return "readonlyFocused";
10878
+ if (cell.isFocused && !cell.isInSelection) return "readonlyFocused";
10764
10879
  if (cell.isInSelection) return "selected";
10765
10880
  return "readonly";
10766
10881
  }
10767
10882
  if (cell.isEditing) return "editing";
10768
- if (cell.isFocused && !hasRange) return "focused";
10883
+ if (cell.isFocused && !cell.isInSelection) return "focused";
10769
10884
  if (cell.isInSelection) return "selected";
10770
10885
  return "display";
10771
10886
  }
@@ -10803,7 +10918,7 @@ const CellWrapper = (0, mobx_react_lite.observer)(({ cell, children, onDoubleCli
10803
10918
  const deferredEdit = useDeferredMenuEdit(onStartEdit ?? onDoubleClick);
10804
10919
  const state = getCellState(cell);
10805
10920
  const selectionEdges = cell.selectionEdges;
10806
- const isAnchorInRange = cell.isAnchor && cell.hasRangeSelection;
10921
+ const isAnchorInRange = cell.isAnchor && cell.isInSelection;
10807
10922
  const navVersion = cell.navigationVersion;
10808
10923
  (0, react.useEffect)(() => {
10809
10924
  if (!cellRef.current) return;
@@ -10966,61 +11081,68 @@ function useTextareaCell(cell) {
10966
11081
  };
10967
11082
  }, []);
10968
11083
  const trigger = cell.editTrigger;
11084
+ const clickOffsetValue = trigger?.type === "doubleClick" ? trigger.clickOffset : void 0;
11085
+ const appendCharValue = trigger?.type === "char" ? trigger.char : void 0;
11086
+ const startEditing = (0, react.useCallback)((clientX) => {
11087
+ if (!cell.isEditable) return;
11088
+ const offset = clientX === void 0 ? void 0 : getClickOffset(textRef.current, cell.displayValue, clientX);
11089
+ cell.startEditWithDoubleClick(offset);
11090
+ }, [cell]);
11091
+ const handleTypeChar = (0, react.useCallback)((char) => {
11092
+ if (!cell.isEditable) return;
11093
+ cell.startEditWithChar(char);
11094
+ }, [cell]);
11095
+ const handleCommitted = (0, react.useCallback)(() => {}, []);
11096
+ const handleCancel = (0, react.useCallback)(() => {
11097
+ cell.cancelEdit();
11098
+ }, [cell]);
11099
+ const handleStartEditFromKeyboard = (0, react.useCallback)(() => {
11100
+ if (!cell.isEditable) return;
11101
+ cell.startEdit();
11102
+ }, [cell]);
11103
+ const trimValue = (0, react.useCallback)((localValue) => {
11104
+ let trimmed = localValue;
11105
+ while (trimmed.endsWith("\n")) trimmed = trimmed.slice(0, -1);
11106
+ return trimmed;
11107
+ }, []);
10969
11108
  return {
10970
11109
  cellRef,
10971
11110
  textRef,
10972
11111
  getEditPosition,
10973
- clickOffsetValue: trigger?.type === "doubleClick" ? trigger.clickOffset : void 0,
10974
- appendCharValue: trigger?.type === "char" ? trigger.char : void 0,
10975
- startEditing: (0, react.useCallback)((clientX) => {
10976
- if (!cell.isEditable) return;
10977
- const offset = clientX === void 0 ? void 0 : getClickOffset(textRef.current, cell.displayValue, clientX);
10978
- cell.startEditWithDoubleClick(offset);
10979
- }, [cell]),
10980
- handleTypeChar: (0, react.useCallback)((char) => {
10981
- if (!cell.isEditable) return;
10982
- cell.startEditWithChar(char);
10983
- }, [cell]),
10984
- handleCommitted: (0, react.useCallback)(() => {}, []),
10985
- handleCancel: (0, react.useCallback)(() => {
10986
- cell.cancelEdit();
10987
- }, [cell]),
10988
- handleStartEditFromKeyboard: (0, react.useCallback)(() => {
10989
- if (!cell.isEditable) return;
10990
- cell.startEdit();
10991
- }, [cell])
11112
+ clickOffsetValue,
11113
+ appendCharValue,
11114
+ startEditing,
11115
+ handleTypeChar,
11116
+ handleCommit: (0, react.useCallback)((localValue) => {
11117
+ const trimmed = trimValue(localValue);
11118
+ if (trimmed === cell.displayValue) cell.cancelEdit();
11119
+ else cell.commitEdit(trimmed);
11120
+ handleCommitted();
11121
+ }, [
11122
+ cell,
11123
+ handleCommitted,
11124
+ trimValue
11125
+ ]),
11126
+ handleCommitEnter: (0, react.useCallback)((localValue) => {
11127
+ const trimmed = trimValue(localValue);
11128
+ if (trimmed === cell.displayValue) cell.commitEditAndMoveDown();
11129
+ else cell.commitEditAndMoveDown(trimmed);
11130
+ handleCommitted();
11131
+ }, [
11132
+ cell,
11133
+ handleCommitted,
11134
+ trimValue
11135
+ ]),
11136
+ handleCommitted,
11137
+ handleCancel,
11138
+ handleStartEditFromKeyboard
10992
11139
  };
10993
11140
  }
10994
11141
 
10995
11142
  //#endregion
10996
11143
  //#region src/table-editor/Table/ui/Cell/StringCell.tsx
10997
11144
  const StringCell = (0, mobx_react_lite.observer)(({ cell }) => {
10998
- const { cellRef, textRef, getEditPosition, clickOffsetValue, appendCharValue, startEditing, handleTypeChar, handleCommitted, handleCancel, handleStartEditFromKeyboard } = useTextareaCell(cell);
10999
- const trimValue = (0, react.useCallback)((localValue) => {
11000
- let trimmed = localValue;
11001
- while (trimmed.endsWith("\n")) trimmed = trimmed.slice(0, -1);
11002
- return trimmed;
11003
- }, []);
11004
- const handleCommit = (0, react.useCallback)((localValue) => {
11005
- const trimmed = trimValue(localValue);
11006
- if (trimmed !== cell.displayValue) cell.commitEdit(trimmed);
11007
- else cell.cancelEdit();
11008
- handleCommitted();
11009
- }, [
11010
- cell,
11011
- handleCommitted,
11012
- trimValue
11013
- ]);
11014
- const handleCommitEnter = (0, react.useCallback)((localValue) => {
11015
- const trimmed = trimValue(localValue);
11016
- if (trimmed === cell.displayValue) cell.commitEditAndMoveDown();
11017
- else cell.commitEditAndMoveDown(trimmed);
11018
- handleCommitted();
11019
- }, [
11020
- cell,
11021
- handleCommitted,
11022
- trimValue
11023
- ]);
11145
+ const { cellRef, textRef, getEditPosition, clickOffsetValue, appendCharValue, startEditing, handleTypeChar, handleCommit, handleCommitEnter, handleCancel, handleStartEditFromKeyboard } = useTextareaCell(cell);
11024
11146
  const editPosition = cell.isEditing ? getEditPosition() : null;
11025
11147
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Box, {
11026
11148
  ref: cellRef,
@@ -11261,19 +11383,144 @@ const ForeignKeyCell = (0, mobx_react_lite.observer)(({ cell, onSearchForeignKey
11261
11383
 
11262
11384
  //#endregion
11263
11385
  //#region src/table-editor/Table/ui/Cell/FileCell.tsx
11264
- const FileCell = (0, mobx_react_lite.observer)(({ cell }) => {
11265
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CellWrapper, {
11266
- cell,
11267
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.HStack, {
11268
- gap: 1,
11269
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_icons_pi.PiFileBold, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Text, {
11270
- whiteSpace: "nowrap",
11271
- textOverflow: "ellipsis",
11272
- overflow: "hidden",
11273
- fontWeight: "300",
11274
- children: cell.displayValue
11275
- })]
11276
- })
11386
+ const FileCell = (0, mobx_react_lite.observer)(({ cell, onUploadFile, onOpenFile }) => {
11387
+ const fileInputRef = (0, react.useRef)(null);
11388
+ const fileData = cell.fileData;
11389
+ const status = fileData?.status ?? "";
11390
+ const fileId = fileData?.fileId ?? "";
11391
+ const url = fileData?.url ?? "";
11392
+ const mimeType = fileData?.mimeType ?? "";
11393
+ const width = fileData?.width ?? 0;
11394
+ const height = fileData?.height ?? 0;
11395
+ const isReadonly = cell.isReadOnly;
11396
+ const isEditing = cell.isEditing;
11397
+ const availablePreview = mimeType.startsWith("image/");
11398
+ const showViewFile = Boolean(url);
11399
+ const showUploadFile = !isReadonly && Boolean(onUploadFile) && (status === "ready" || status === "uploaded");
11400
+ const showInfo = !showViewFile && !showUploadFile && isReadonly;
11401
+ const { cellRef, textRef, getEditPosition, clickOffsetValue, appendCharValue, startEditing, handleTypeChar, handleCommit, handleCommitEnter, handleCancel, handleStartEditFromKeyboard } = useTextareaCell(cell);
11402
+ const handleFileChange = (0, react.useCallback)(async (event) => {
11403
+ const file = event.target.files?.[0];
11404
+ if (!file) return;
11405
+ event.target.value = "";
11406
+ try {
11407
+ const result = await onUploadFile?.({
11408
+ rowId: cell.rowId,
11409
+ fileId,
11410
+ file
11411
+ });
11412
+ if (result) cell.commitFileUpload(result);
11413
+ } catch {}
11414
+ }, [
11415
+ onUploadFile,
11416
+ fileId,
11417
+ cell
11418
+ ]);
11419
+ const handleOpenFile = (0, react.useCallback)(() => {
11420
+ if (onOpenFile) onOpenFile(url);
11421
+ else window.open(url, "_blank", "noopener,noreferrer");
11422
+ }, [onOpenFile, url]);
11423
+ const handleUploadClick = (0, react.useCallback)((e) => {
11424
+ e.stopPropagation();
11425
+ fileInputRef.current?.click();
11426
+ }, []);
11427
+ const editPosition = isEditing ? getEditPosition() : null;
11428
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Box, {
11429
+ ref: cellRef,
11430
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CellWrapper, {
11431
+ cell,
11432
+ onDoubleClick: cell.isEditable ? startEditing : void 0,
11433
+ onStartEdit: cell.isEditable ? handleStartEditFromKeyboard : void 0,
11434
+ onTypeChar: cell.isEditable ? handleTypeChar : void 0,
11435
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.HStack, {
11436
+ gap: 1,
11437
+ width: "100%",
11438
+ justifyContent: "space-between",
11439
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Text, {
11440
+ ref: textRef,
11441
+ whiteSpace: "nowrap",
11442
+ textOverflow: "ellipsis",
11443
+ overflow: "hidden",
11444
+ fontWeight: "300",
11445
+ flex: 1,
11446
+ minWidth: 0,
11447
+ children: cell.displayValue
11448
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Flex, {
11449
+ className: "file-action-buttons",
11450
+ alignItems: "center",
11451
+ flexShrink: 0,
11452
+ opacity: 0,
11453
+ transition: "opacity 0.15s ease",
11454
+ css: { "td:hover &, &[data-visible]": { opacity: 1 } },
11455
+ ...showInfo ? { "data-visible": true } : {},
11456
+ children: [
11457
+ showInfo && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tooltip, {
11458
+ openDelay: 50,
11459
+ closeDelay: 50,
11460
+ content: "The file was not uploaded in this revision",
11461
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Flex, {
11462
+ width: "24px",
11463
+ height: "24px",
11464
+ alignItems: "center",
11465
+ justifyContent: "center",
11466
+ color: "gray.400",
11467
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_icons_pi.PiInfo, {})
11468
+ })
11469
+ }),
11470
+ showViewFile && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileHoverCard, {
11471
+ url,
11472
+ availablePreview,
11473
+ width,
11474
+ height,
11475
+ onClick: handleOpenFile,
11476
+ dataTestId: `cell-file-open-${cell.rowId}-${cell.field}`
11477
+ }),
11478
+ showUploadFile && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Box, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
11479
+ ref: fileInputRef,
11480
+ type: "file",
11481
+ onChange: handleFileChange,
11482
+ style: { display: "none" },
11483
+ "data-testid": `cell-file-input-${cell.rowId}-${cell.field}`
11484
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.IconButton, {
11485
+ "aria-label": "Upload file",
11486
+ variant: "ghost",
11487
+ size: "2xs",
11488
+ color: showViewFile ? "gray.300" : "gray.500",
11489
+ _hover: {
11490
+ bg: "gray.100",
11491
+ color: "black"
11492
+ },
11493
+ onClick: handleUploadClick,
11494
+ "data-testid": `cell-file-upload-${cell.rowId}-${cell.field}`,
11495
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_icons_pi.PiUploadThin, {})
11496
+ })] }),
11497
+ !showInfo && !showViewFile && !showUploadFile && !isReadonly && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tooltip, {
11498
+ openDelay: 50,
11499
+ closeDelay: 50,
11500
+ content: "Save the row first, then upload the file",
11501
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Flex, {
11502
+ width: "24px",
11503
+ height: "24px",
11504
+ alignItems: "center",
11505
+ justifyContent: "center",
11506
+ color: "gray.400",
11507
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_icons_pi.PiInfo, {})
11508
+ })
11509
+ })
11510
+ ]
11511
+ })]
11512
+ })
11513
+ }), editPosition && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CellTextareaEditor, {
11514
+ value: cell.displayValue,
11515
+ position: editPosition,
11516
+ clickOffset: clickOffsetValue,
11517
+ appendChar: appendCharValue,
11518
+ autoHeight: true,
11519
+ onCommit: handleCommit,
11520
+ onCommitEnter: handleCommitEnter,
11521
+ onCancel: handleCancel,
11522
+ testId: "file-cell-input"
11523
+ })]
11277
11524
  });
11278
11525
  });
11279
11526
 
@@ -11311,7 +11558,7 @@ const ReadonlyCell = (0, mobx_react_lite.observer)(({ cell }) => {
11311
11558
 
11312
11559
  //#endregion
11313
11560
  //#region src/table-editor/Table/ui/Cell/CellRenderer.tsx
11314
- const CellRenderer = (0, mobx_react_lite.observer)(({ cell, onSearchForeignKey }) => {
11561
+ const CellRenderer = (0, mobx_react_lite.observer)(({ cell, onSearchForeignKey, onUploadFile, onOpenFile }) => {
11315
11562
  switch (cell.column.fieldType) {
11316
11563
  case FilterFieldType.String: return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StringCell, { cell });
11317
11564
  case FilterFieldType.Number: return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberCell, { cell });
@@ -11320,7 +11567,11 @@ const CellRenderer = (0, mobx_react_lite.observer)(({ cell, onSearchForeignKey }
11320
11567
  cell,
11321
11568
  onSearchForeignKey
11322
11569
  });
11323
- case FilterFieldType.File: return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileCell, { cell });
11570
+ case FilterFieldType.File: return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileCell, {
11571
+ cell,
11572
+ onUploadFile,
11573
+ onOpenFile
11574
+ });
11324
11575
  case FilterFieldType.DateTime: return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DateTimeCell, { cell });
11325
11576
  default: return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadonlyCell, { cell });
11326
11577
  }
@@ -11489,6 +11740,7 @@ const ResizeHandle = (0, mobx_react_lite.observer)(({ field, columnsModel }) =>
11489
11740
  };
11490
11741
  const cleanup = () => {
11491
11742
  cancelAnimationFrame(rafRef.current);
11743
+ columnsModel.commitColumnWidth();
11492
11744
  setIsResizing(false);
11493
11745
  document.body.style.cursor = "";
11494
11746
  document.body.style.userSelect = "";
@@ -12018,7 +12270,7 @@ const AddColumnButton = (0, mobx_react_lite.observer)(({ columnsModel }) => {
12018
12270
  const SELECTION_COLUMN_WIDTH$1 = 40;
12019
12271
  const ADD_COLUMN_BUTTON_WIDTH$1 = 40;
12020
12272
  const BOTTOM_BORDER_SHADOW$1 = "inset 0 -1px 0 0 var(--chakra-colors-gray-100)";
12021
- const HeaderRow = (0, mobx_react_lite.observer)(({ columnsModel, sortModel, filterModel, onCopyPath, showSelection, showLeftShadow, showRightShadow }) => {
12273
+ const HeaderRow = (0, mobx_react_lite.observer)(({ columnsModel, sortModel, filterModel, onCopyPath, showSelection, scrollShadow }) => {
12022
12274
  const selectionWidth = showSelection ? SELECTION_COLUMN_WIDTH$1 : 0;
12023
12275
  const addColumnStickyRight = columnsModel.hasHiddenColumns;
12024
12276
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Box, {
@@ -12045,8 +12297,8 @@ const HeaderRow = (0, mobx_react_lite.observer)(({ columnsModel, sortModel, filt
12045
12297
  filterModel,
12046
12298
  onCopyPath,
12047
12299
  stickyPosition: getStickyPosition(col.field, columnsModel, selectionWidth, addColumnStickyRight),
12048
- showLeftShadow,
12049
- showRightShadow
12300
+ showLeftShadow: scrollShadow?.showLeftShadow,
12301
+ showRightShadow: scrollShadow?.showRightShadow
12050
12302
  }, col.field);
12051
12303
  }),
12052
12304
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Box, {
@@ -12291,7 +12543,7 @@ function getCellBoxShadow(isSticky, side) {
12291
12543
  if (!isSticky) return BOTTOM_BORDER_SHADOW;
12292
12544
  return `${BOTTOM_BORDER_SHADOW}, ${side === "left" ? "inset -1px 0 0 0 var(--chakra-colors-gray-100)" : "inset 1px 0 0 0 var(--chakra-colors-gray-100)"}`;
12293
12545
  }
12294
- const DataRow = (0, mobx_react_lite.observer)(({ row, columnsModel, showSelection, showLeftShadow, showRightShadow, onSearchForeignKey, onOpenRow, onSelectRow, onDuplicateRow, onDeleteRow }) => {
12546
+ const DataRow = (0, mobx_react_lite.observer)(({ row, columnsModel, showSelection, scrollShadow, onSearchForeignKey, onUploadFile, onOpenFile, onOpenRow, onSelectRow, onDuplicateRow, onDeleteRow }) => {
12295
12547
  const hasRowActions = Boolean(onOpenRow || onSelectRow || onDuplicateRow || onDeleteRow);
12296
12548
  const selectionWidth = showSelection ? SELECTION_COLUMN_WIDTH : 0;
12297
12549
  const addColumnStickyRight = columnsModel.hasHiddenColumns;
@@ -12306,7 +12558,7 @@ const DataRow = (0, mobx_react_lite.observer)(({ row, columnsModel, showSelectio
12306
12558
  const cellVM = row.getCellVM(col);
12307
12559
  const isFirstColumn = index === 0;
12308
12560
  const showOverlay = isFirstColumn && hasRowActions && !cellVM.isEditing;
12309
- const sticky = computeStickyProps(col, columnsModel, selectionWidth, addColOffset, showLeftShadow, showRightShadow);
12561
+ const sticky = computeStickyProps(col, columnsModel, selectionWidth, addColOffset, scrollShadow?.showLeftShadow, scrollShadow?.showRightShadow);
12310
12562
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Box, {
12311
12563
  as: "td",
12312
12564
  width: sticky.colWidth,
@@ -12327,11 +12579,15 @@ const DataRow = (0, mobx_react_lite.observer)(({ row, columnsModel, showSelectio
12327
12579
  overflow: "hidden",
12328
12580
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CellRenderer, {
12329
12581
  cell: cellVM,
12330
- onSearchForeignKey
12582
+ onSearchForeignKey,
12583
+ onUploadFile,
12584
+ onOpenFile
12331
12585
  })
12332
12586
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CellRenderer, {
12333
12587
  cell: cellVM,
12334
- onSearchForeignKey
12588
+ onSearchForeignKey,
12589
+ onUploadFile,
12590
+ onOpenFile
12335
12591
  }), showOverlay && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowActionOverlay, {
12336
12592
  rowId: row.rowId,
12337
12593
  onOpen: onOpenRow,
@@ -12483,16 +12739,29 @@ const TableRowComponent = ({ "data-index": index, context, style, children, ...p
12483
12739
 
12484
12740
  //#endregion
12485
12741
  //#region src/table-editor/Table/ui/hooks/useScrollShadow.ts
12486
- const INITIAL_STATE = {
12487
- showLeftShadow: false,
12488
- showRightShadow: false
12742
+ var ScrollShadowModel = class {
12743
+ showLeftShadow = false;
12744
+ showRightShadow = false;
12745
+ constructor() {
12746
+ (0, mobx.makeAutoObservable)(this);
12747
+ }
12748
+ update(left, right) {
12749
+ this.showLeftShadow = left;
12750
+ this.showRightShadow = right;
12751
+ }
12752
+ reset() {
12753
+ this.showLeftShadow = false;
12754
+ this.showRightShadow = false;
12755
+ }
12489
12756
  };
12490
12757
  function getScrollElement(target) {
12491
12758
  if (target instanceof HTMLElement) return target;
12492
12759
  return document.scrollingElement;
12493
12760
  }
12494
12761
  function useScrollShadow() {
12495
- const [state, setState] = (0, react.useState)(INITIAL_STATE);
12762
+ const modelRef = (0, react.useRef)(null);
12763
+ if (!modelRef.current) modelRef.current = new ScrollShadowModel();
12764
+ const model = modelRef.current;
12496
12765
  const targetRef = (0, react.useRef)(null);
12497
12766
  const rafRef = (0, react.useRef)(0);
12498
12767
  const update = (0, react.useCallback)(() => {
@@ -12502,11 +12771,10 @@ function useScrollShadow() {
12502
12771
  if (!el) return;
12503
12772
  const scrollLeft = el.scrollLeft;
12504
12773
  const maxScroll = el.scrollWidth - el.clientWidth;
12505
- setState({
12506
- showLeftShadow: scrollLeft > 0,
12507
- showRightShadow: maxScroll > 1 && scrollLeft < maxScroll - 1
12774
+ (0, mobx.runInAction)(() => {
12775
+ model.update(scrollLeft > 0, maxScroll > 1 && scrollLeft < maxScroll - 1);
12508
12776
  });
12509
- }, []);
12777
+ }, [model]);
12510
12778
  const handleScroll = (0, react.useCallback)(() => {
12511
12779
  if (rafRef.current) cancelAnimationFrame(rafRef.current);
12512
12780
  rafRef.current = requestAnimationFrame(update);
@@ -12520,9 +12788,15 @@ function useScrollShadow() {
12520
12788
  rafRef.current = requestAnimationFrame(update);
12521
12789
  } else {
12522
12790
  targetRef.current = null;
12523
- setState(INITIAL_STATE);
12791
+ (0, mobx.runInAction)(() => {
12792
+ model.reset();
12793
+ });
12524
12794
  }
12525
- }, [handleScroll, update]);
12795
+ }, [
12796
+ handleScroll,
12797
+ update,
12798
+ model
12799
+ ]);
12526
12800
  (0, react.useEffect)(() => {
12527
12801
  return () => {
12528
12802
  const prev = targetRef.current;
@@ -12531,7 +12805,7 @@ function useScrollShadow() {
12531
12805
  };
12532
12806
  }, [handleScroll]);
12533
12807
  return {
12534
- state,
12808
+ model,
12535
12809
  setScrollerRef
12536
12810
  };
12537
12811
  }
@@ -12542,11 +12816,11 @@ const baseComponents = {
12542
12816
  Table: TableComponent,
12543
12817
  TableRow: TableRowComponent
12544
12818
  };
12545
- const TableWidget = (0, mobx_react_lite.observer)(({ rows, columnsModel, cellFSM, selection, sortModel, filterModel, onSearchForeignKey, onOpenRow, onDeleteSelected, onDuplicateSelected, onDeleteRow, onDuplicateRow, onCopyPath, onEndReached, isLoadingMore, useWindowScroll: useWindowScrollProp }) => {
12819
+ const TableWidget = (0, mobx_react_lite.observer)(({ rows, columnsModel, cellFSM, selection, sortModel, filterModel, onSearchForeignKey, onUploadFile, onOpenFile, onOpenRow, onDeleteSelected, onDuplicateSelected, onDeleteRow, onDuplicateRow, onCopyPath, onEndReached, isLoadingMore, useWindowScroll: useWindowScrollProp }) => {
12546
12820
  const showSelection = selection.isSelectionMode;
12547
12821
  const allRowIds = rows.map((r) => r.rowId);
12548
12822
  const [deleteConfirm, setDeleteConfirm] = (0, react.useState)(null);
12549
- const { state: scrollShadow, setScrollerRef } = useScrollShadow();
12823
+ const { model: scrollShadow, setScrollerRef } = useScrollShadow();
12550
12824
  const handleSelectRow = (0, react.useCallback)((rowId) => {
12551
12825
  selection.enterSelectionMode(rowId);
12552
12826
  }, [selection]);
@@ -12644,9 +12918,10 @@ const TableWidget = (0, mobx_react_lite.observer)(({ rows, columnsModel, cellFSM
12644
12918
  row,
12645
12919
  columnsModel,
12646
12920
  showSelection,
12647
- showLeftShadow: scrollShadow.showLeftShadow,
12648
- showRightShadow: scrollShadow.showRightShadow,
12921
+ scrollShadow,
12649
12922
  onSearchForeignKey,
12923
+ onUploadFile,
12924
+ onOpenFile,
12650
12925
  onOpenRow,
12651
12926
  onSelectRow: canSelect ? handleSelectRow : void 0,
12652
12927
  onDuplicateRow: canDuplicateRow ? onDuplicateRow : void 0,
@@ -12654,9 +12929,10 @@ const TableWidget = (0, mobx_react_lite.observer)(({ rows, columnsModel, cellFSM
12654
12929
  }), [
12655
12930
  columnsModel,
12656
12931
  showSelection,
12657
- scrollShadow.showLeftShadow,
12658
- scrollShadow.showRightShadow,
12932
+ scrollShadow,
12659
12933
  onSearchForeignKey,
12934
+ onUploadFile,
12935
+ onOpenFile,
12660
12936
  onOpenRow,
12661
12937
  canSelect,
12662
12938
  canDeleteRow,
@@ -12671,16 +12947,14 @@ const TableWidget = (0, mobx_react_lite.observer)(({ rows, columnsModel, cellFSM
12671
12947
  filterModel,
12672
12948
  onCopyPath,
12673
12949
  showSelection,
12674
- showLeftShadow: scrollShadow.showLeftShadow,
12675
- showRightShadow: scrollShadow.showRightShadow
12950
+ scrollShadow
12676
12951
  }), [
12677
12952
  columnsModel,
12678
12953
  sortModel,
12679
12954
  filterModel,
12680
12955
  onCopyPath,
12681
12956
  showSelection,
12682
- scrollShadow.showLeftShadow,
12683
- scrollShadow.showRightShadow
12957
+ scrollShadow
12684
12958
  ]);
12685
12959
  const virtuosoContext = (0, react.useMemo)(() => ({ rows }), [rows]);
12686
12960
  const totalColumns = columnsModel.visibleColumns.length + (showSelection ? 4 : 3);
@@ -12934,6 +13208,67 @@ var ViewSettingsBadgeModel = class {
12934
13208
  }
12935
13209
  };
12936
13210
 
13211
+ //#endregion
13212
+ //#region src/table-editor/TableEditor/model/SchemaContext.ts
13213
+ const SYSTEM_REF_SCHEMAS = {
13214
+ [_revisium_schema_toolkit.SystemSchemaIds.File]: _revisium_schema_toolkit.fileSchema,
13215
+ [_revisium_schema_toolkit.SystemSchemaIds.RowId]: _revisium_schema_toolkit.rowIdSchema,
13216
+ [_revisium_schema_toolkit.SystemSchemaIds.RowCreatedAt]: _revisium_schema_toolkit.rowCreatedAtSchema,
13217
+ [_revisium_schema_toolkit.SystemSchemaIds.RowCreatedId]: _revisium_schema_toolkit.rowCreatedIdSchema,
13218
+ [_revisium_schema_toolkit.SystemSchemaIds.RowVersionId]: _revisium_schema_toolkit.rowVersionIdSchema,
13219
+ [_revisium_schema_toolkit.SystemSchemaIds.RowPublishedAt]: _revisium_schema_toolkit.rowPublishedAtSchema,
13220
+ [_revisium_schema_toolkit.SystemSchemaIds.RowUpdatedAt]: _revisium_schema_toolkit.rowUpdatedAtSchema,
13221
+ [_revisium_schema_toolkit.SystemSchemaIds.RowHash]: _revisium_schema_toolkit.rowHashSchema,
13222
+ [_revisium_schema_toolkit.SystemSchemaIds.RowSchemaHash]: _revisium_schema_toolkit.rowSchemaHashSchema
13223
+ };
13224
+ function buildRowSchema(dataSchema) {
13225
+ const systemProperties = {};
13226
+ for (const sf of SYSTEM_FIELDS) systemProperties[sf.id] = { $ref: sf.ref };
13227
+ return {
13228
+ type: "object",
13229
+ properties: {
13230
+ ...systemProperties,
13231
+ ...dataSchema.properties
13232
+ },
13233
+ additionalProperties: false,
13234
+ required: [...Object.keys(systemProperties), ...dataSchema.required ?? []]
13235
+ };
13236
+ }
13237
+ var SchemaContext = class {
13238
+ _allColumns = [];
13239
+ _dataSchema = null;
13240
+ _fullRefSchemas = {};
13241
+ _rootNode = null;
13242
+ get allColumns() {
13243
+ return this._allColumns;
13244
+ }
13245
+ get sortableFields() {
13246
+ return this._allColumns.filter((col) => !col.isDeprecated && col.fieldType !== FilterFieldType.File);
13247
+ }
13248
+ get filterableFields() {
13249
+ return this.sortableFields;
13250
+ }
13251
+ get dataSchema() {
13252
+ return this._dataSchema;
13253
+ }
13254
+ get fullRefSchemas() {
13255
+ return this._fullRefSchemas;
13256
+ }
13257
+ get rootNode() {
13258
+ return this._rootNode;
13259
+ }
13260
+ init(dataSchema, refSchemas) {
13261
+ this._dataSchema = dataSchema;
13262
+ this._fullRefSchemas = {
13263
+ ...SYSTEM_REF_SCHEMAS,
13264
+ ...refSchemas
13265
+ };
13266
+ const rowSchema = buildRowSchema(dataSchema);
13267
+ this._rootNode = new _revisium_schema_toolkit.SchemaParser().parse(rowSchema, this._fullRefSchemas);
13268
+ this._allColumns = extractColumns(this._rootNode);
13269
+ }
13270
+ };
13271
+
12937
13272
  //#endregion
12938
13273
  //#region src/table-editor/TableEditor/model/TableEditorCore.ts
12939
13274
  const DEFAULT_PAGE_SIZE = 50;
@@ -12951,7 +13286,7 @@ var TableEditorCore = class {
12951
13286
  _breadcrumbs;
12952
13287
  _callbacks;
12953
13288
  _tableId;
12954
- _schema = null;
13289
+ _schemaContext = new SchemaContext();
12955
13290
  _readonly = false;
12956
13291
  _rows = [];
12957
13292
  _isBootstrapping = true;
@@ -13060,13 +13395,13 @@ var TableEditorCore = class {
13060
13395
  try {
13061
13396
  const meta = await this._dataSource.fetchMetadata();
13062
13397
  (0, mobx.runInAction)(() => {
13063
- this._schema = meta.schema;
13398
+ this._schemaContext.init(meta.dataSchema, meta.refSchemas);
13064
13399
  this._readonly = meta.readonly;
13065
13400
  this.viewBadge.setCanSave(!meta.readonly);
13066
13401
  });
13067
- this.columns.init(meta.columns);
13068
- this.sorts.init(this.columns.sortableFields);
13069
- this.filters.init(this.columns.filterableFields);
13402
+ this.columns.init(this._schemaContext.allColumns);
13403
+ this.sorts.init(this._schemaContext.sortableFields);
13404
+ this.filters.init(this._schemaContext.filterableFields);
13070
13405
  if (meta.viewState) this.applyViewState(meta.viewState);
13071
13406
  this._saveViewSnapshot();
13072
13407
  await this._loadRows();
@@ -13117,15 +13452,26 @@ var TableEditorCore = class {
13117
13452
  this._updateNavigationContext();
13118
13453
  }
13119
13454
  _createRowVMs(rawRows) {
13120
- if (!this._schema) return [];
13121
- return (0, _revisium_schema_toolkit.createTableModel)({
13455
+ const dataSchema = this._schemaContext.dataSchema;
13456
+ if (!dataSchema) return [];
13457
+ const tableModel = (0, _revisium_schema_toolkit.createTableModel)({
13122
13458
  tableId: this._tableId,
13123
- schema: this._schema,
13459
+ schema: dataSchema,
13124
13460
  rows: rawRows.map((r) => ({
13125
13461
  rowId: r.rowId,
13126
13462
  data: r.data
13127
- }))
13128
- }).rows.map((rowModel) => new RowVM(rowModel, rowModel.rowId, this.cellFSM, this.selection, (rowId, field, value, previousValue) => this._commitCell(rowId, field, value, previousValue)));
13463
+ })),
13464
+ refSchemas: this._schemaContext.fullRefSchemas
13465
+ });
13466
+ const systemValuesMap = /* @__PURE__ */ new Map();
13467
+ for (const raw of rawRows) systemValuesMap.set(raw.rowId, this._buildSystemValues(raw));
13468
+ return tableModel.rows.map((rowModel) => new RowVM(rowModel, rowModel.rowId, this.cellFSM, this.selection, (rowId, field, value, previousValue) => this._commitCell(rowId, field, value, previousValue), systemValuesMap.get(rowModel.rowId)));
13469
+ }
13470
+ _buildSystemValues(rawRow) {
13471
+ return {
13472
+ ...rawRow.systemFields,
13473
+ id: rawRow.rowId
13474
+ };
13129
13475
  }
13130
13476
  async _commitCell(rowId, field, value, previousValue) {
13131
13477
  const result = (await this._dataSource.patchCells([{
@@ -13138,6 +13484,7 @@ var TableEditorCore = class {
13138
13484
  if (row && previousValue !== void 0) {
13139
13485
  const node = row.rowModel.get(field);
13140
13486
  if (node?.isPrimitive()) node.setValue(previousValue);
13487
+ else if (node?.isObject()) node.setValue(previousValue, { internal: true });
13141
13488
  }
13142
13489
  });
13143
13490
  }
@@ -13192,37 +13539,39 @@ var TableEditorCore = class {
13192
13539
  //#region src/table-editor/TableEditor/model/MockDataSource.ts
13193
13540
  var MockDataSource = class {
13194
13541
  _allRows;
13195
- _schema;
13196
- _columns;
13542
+ _dataSchema;
13197
13543
  _viewState;
13198
13544
  _readonly;
13199
13545
  _failPatches;
13546
+ _refSchemas;
13200
13547
  fetchMetadataLog = [];
13201
13548
  fetchLog = [];
13202
13549
  patchLog = [];
13203
13550
  deleteLog = [];
13204
13551
  saveViewLog = [];
13205
13552
  constructor(params) {
13206
- this._schema = params.schema;
13207
- this._columns = params.columns;
13553
+ this._dataSchema = params.dataSchema;
13208
13554
  this._allRows = [...params.rows];
13209
13555
  this._viewState = params.viewState ?? null;
13210
13556
  this._readonly = params.readonly ?? false;
13211
13557
  this._failPatches = params.failPatches ?? /* @__PURE__ */ new Set();
13558
+ this._refSchemas = params.refSchemas;
13212
13559
  }
13213
- static createRow(rowId, data) {
13214
- return {
13560
+ static createRow(rowId, data, systemFields) {
13561
+ const row = {
13215
13562
  rowId,
13216
13563
  data: { ...data }
13217
13564
  };
13565
+ if (systemFields) row.systemFields = systemFields;
13566
+ return row;
13218
13567
  }
13219
13568
  fetchMetadata() {
13220
13569
  this.fetchMetadataLog.push(true);
13221
13570
  return Promise.resolve({
13222
- schema: this._schema,
13223
- columns: this._columns,
13571
+ dataSchema: this._dataSchema,
13224
13572
  viewState: this._viewState,
13225
- readonly: this._readonly
13573
+ readonly: this._readonly,
13574
+ refSchemas: this._refSchemas
13226
13575
  });
13227
13576
  }
13228
13577
  fetchRows(query) {
@@ -13404,7 +13753,6 @@ const TableEditor = (0, mobx_react_lite.observer)(({ viewModel, useWindowScroll
13404
13753
  height: "100%",
13405
13754
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Spinner, {})
13406
13755
  });
13407
- const columns = viewModel.columns.visibleColumns;
13408
13756
  const isReadonly = viewModel.readonly;
13409
13757
  const { breadcrumbs, callbacks } = viewModel;
13410
13758
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Box, {
@@ -13434,11 +13782,11 @@ const TableEditor = (0, mobx_react_lite.observer)(({ viewModel, useWindowScroll
13434
13782
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchWidget, { model: viewModel.search }),
13435
13783
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterWidget, {
13436
13784
  model: viewModel.filters,
13437
- availableFields: columns
13785
+ availableFields: viewModel.columns.filterableFields
13438
13786
  }),
13439
13787
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SortingsWidget, {
13440
13788
  model: viewModel.sorts,
13441
- availableFields: columns,
13789
+ availableFields: viewModel.columns.sortableFields,
13442
13790
  onChange: noop
13443
13791
  })
13444
13792
  ]
@@ -13461,6 +13809,8 @@ const TableEditor = (0, mobx_react_lite.observer)(({ viewModel, useWindowScroll
13461
13809
  onDuplicateRow: isReadonly ? void 0 : callbacks.onDuplicateRow,
13462
13810
  onDeleteSelected: isReadonly ? void 0 : (ids) => viewModel.deleteRows(ids),
13463
13811
  onSearchForeignKey: callbacks.onSearchForeignKey,
13812
+ onUploadFile: isReadonly ? void 0 : callbacks.onUploadFile,
13813
+ onOpenFile: callbacks.onOpenFile,
13464
13814
  onCopyPath: callbacks.onCopyPath,
13465
13815
  useWindowScroll
13466
13816
  })
@@ -13528,6 +13878,7 @@ exports.SORT_SCHEMA = SORT_SCHEMA;
13528
13878
  exports.SYSTEM_FIELDS = SYSTEM_FIELDS;
13529
13879
  exports.SYSTEM_FIELD_BY_REF = SYSTEM_FIELD_BY_REF;
13530
13880
  exports.SYSTEM_FIELD_IDS = SYSTEM_FIELD_IDS;
13881
+ exports.SchemaContext = SchemaContext;
13531
13882
  exports.SchemaEditorCore = SchemaEditorCore;
13532
13883
  exports.SchemaTypeIds = SchemaTypeIds;
13533
13884
  exports.SearchForeignKey = SearchForeignKey;