@revisium/schema-toolkit-ui 0.6.3 → 0.6.5

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/README.md CHANGED
@@ -236,6 +236,7 @@ All callbacks are optional and passed via `TableEditorOptions.callbacks`:
236
236
  | `onBreadcrumbClick` | `(segment, index) => void` | Navigate on breadcrumb click |
237
237
  | `onCreateRow` | `() => void` | Create a new row (shows "+" button) |
238
238
  | `onOpenRow` | `(rowId: string) => void` | Navigate to row detail view |
239
+ | `onPickRow` | `(rowId: string) => void` | Pick a row (e.g. foreign key selection). When set, the primary row action becomes "Pick" and the table behaves as read-only |
239
240
  | `onDuplicateRow` | `(rowId: string) => void` | Duplicate a row |
240
241
  | `onSearchForeignKey` | `SearchForeignKeySearchFn` | Foreign key search handler |
241
242
  | `onUploadFile` | `(params: { rowId: string; fileId: string; file: File }) => Promise<Record<string, unknown> \| null>` | Upload a file for a file field |
package/dist/index.cjs CHANGED
@@ -5757,21 +5757,25 @@ const Row = ({ node, name, guides, isCollapsible = false, isExpanded = true, onT
5757
5757
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Flex, {
5758
5758
  width: "100%",
5759
5759
  alignItems: "center",
5760
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Flex, { children: [
5761
- !skipDot && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {
5762
- isCollapsed,
5763
- isCollapsible,
5764
- toggleCollapsed: onToggle,
5765
- testId
5766
- }),
5767
- !skipField && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
5768
- name,
5769
- formula,
5770
- description,
5771
- isDeprecated
5772
- }),
5773
- children
5774
- ] }), !skipMore && isCollapsed && collapsedLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(More, {
5760
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Flex, {
5761
+ flex: skipDot && skipField ? 1 : void 0,
5762
+ minWidth: 0,
5763
+ children: [
5764
+ !skipDot && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dot, {
5765
+ isCollapsed,
5766
+ isCollapsible,
5767
+ toggleCollapsed: onToggle,
5768
+ testId
5769
+ }),
5770
+ !skipField && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
5771
+ name,
5772
+ formula,
5773
+ description,
5774
+ isDeprecated
5775
+ }),
5776
+ children
5777
+ ]
5778
+ }), !skipMore && isCollapsed && collapsedLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(More, {
5775
5779
  onClick: onToggle,
5776
5780
  label: collapsedLabel
5777
5781
  })]
@@ -6997,7 +7001,10 @@ var SchemaContext = class {
6997
7001
  const wrapped = wrapDataSchema(dataSchema);
6998
7002
  this._wrappedDataSchema = wrapped;
6999
7003
  const rowSchema = buildRowSchema(wrapped);
7000
- this._rootNode = new _revisium_schema_toolkit.SchemaParser().parse(rowSchema, this._fullRefSchemas);
7004
+ const parser = new _revisium_schema_toolkit.SchemaParser();
7005
+ this._rootNode = parser.parse(rowSchema, this._fullRefSchemas);
7006
+ const tree = (0, _revisium_schema_toolkit.createSchemaTree)(this._rootNode);
7007
+ parser.parseFormulas(tree);
7001
7008
  this._allColumns = extractColumns(this._rootNode);
7002
7009
  }
7003
7010
  };
@@ -10433,13 +10440,15 @@ var CellVM = class {
10433
10440
  _cellFSM;
10434
10441
  _onCommit;
10435
10442
  _systemValues;
10436
- constructor(rowModel, column, rowId, cellFSM, onCommit, systemValues) {
10443
+ _readonly;
10444
+ constructor(rowModel, column, rowId, cellFSM, onCommit, systemValues, readonly) {
10437
10445
  this._rowModel = rowModel;
10438
10446
  this._column = column;
10439
10447
  this._rowId = rowId;
10440
10448
  this._cellFSM = cellFSM;
10441
10449
  this._onCommit = onCommit ?? null;
10442
10450
  this._systemValues = systemValues ?? null;
10451
+ this._readonly = readonly ?? false;
10443
10452
  (0, mobx.makeAutoObservable)(this, { selectionEdges: (0, mobx.computed)({ equals: mobx.comparer.structural }) }, { autoBind: true });
10444
10453
  }
10445
10454
  get field() {
@@ -10476,6 +10485,7 @@ var CellVM = class {
10476
10485
  return "";
10477
10486
  }
10478
10487
  get isReadOnly() {
10488
+ if (this._readonly) return true;
10479
10489
  if (this._systemValues) return true;
10480
10490
  if (this._column.fieldType === FilterFieldType.File) {
10481
10491
  const fileNameNode = this._getFileNameNode();
@@ -10721,14 +10731,16 @@ var RowVM = class {
10721
10731
  _selection;
10722
10732
  _onCellCommit;
10723
10733
  _systemValues;
10734
+ _readonly;
10724
10735
  _cellCache = /* @__PURE__ */ new Map();
10725
- constructor(rowModel, rowId, cellFSM, selection, onCellCommit, systemValues) {
10736
+ constructor(rowModel, rowId, cellFSM, selection, onCellCommit, systemValues, readonly) {
10726
10737
  this._rowModel = rowModel;
10727
10738
  this._rowId = rowId;
10728
10739
  this._cellFSM = cellFSM;
10729
10740
  this._selection = selection;
10730
10741
  this._onCellCommit = onCellCommit ?? null;
10731
10742
  this._systemValues = systemValues ?? {};
10743
+ this._readonly = readonly ?? false;
10732
10744
  (0, mobx.makeAutoObservable)(this, {}, { autoBind: true });
10733
10745
  }
10734
10746
  get rowId() {
@@ -10746,7 +10758,7 @@ var RowVM = class {
10746
10758
  getCellVM(column) {
10747
10759
  const cached = this._cellCache.get(column.field);
10748
10760
  if (cached) return cached;
10749
- const cell = new CellVM(this._rowModel, column, this._rowId, this._cellFSM, this._onCellCommit ?? void 0, column.isSystem ? this._systemValues : void 0);
10761
+ const cell = new CellVM(this._rowModel, column, this._rowId, this._cellFSM, this._onCellCommit ?? void 0, column.isSystem ? this._systemValues : void 0, this._readonly);
10750
10762
  this._cellCache.set(column.field, cell);
10751
10763
  return cell;
10752
10764
  }
@@ -12179,6 +12191,19 @@ const FilterIndicator = (0, mobx_react_lite.observer)(({ field, filterModel }) =
12179
12191
  });
12180
12192
  });
12181
12193
 
12194
+ //#endregion
12195
+ //#region src/table-editor/Table/ui/Header/FormulaIndicator.tsx
12196
+ const FormulaIndicator = ({ column }) => {
12197
+ if (!column.hasFormula) return null;
12198
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Flex, {
12199
+ alignItems: "center",
12200
+ color: "gray.400",
12201
+ flexShrink: 0,
12202
+ "data-testid": `formula-indicator-${column.field}`,
12203
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_icons_pi.PiFunction, { size: 12 })
12204
+ });
12205
+ };
12206
+
12182
12207
  //#endregion
12183
12208
  //#region src/table-editor/Table/ui/Header/SortSubmenu.tsx
12184
12209
  const SortSubmenu = (0, mobx_react_lite.observer)(({ field, sortModel }) => {
@@ -12447,10 +12472,6 @@ const ColumnHeaderMenu = (0, mobx_react_lite.observer)(({ column, columnsModel,
12447
12472
  onClick: handleAddFilter,
12448
12473
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_icons_lu.LuFilter, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Text, { children: "Add filter" })]
12449
12474
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Menu.Separator, {})] }),
12450
- canMove && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MoveColumnSubmenu, {
12451
- field: column.field,
12452
- columnsModel
12453
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Menu.Separator, {})] }),
12454
12475
  isPinned ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Menu.Item, {
12455
12476
  value: "unpin",
12456
12477
  onClick: handleUnpin,
@@ -12468,6 +12489,10 @@ const ColumnHeaderMenu = (0, mobx_react_lite.observer)(({ column, columnsModel,
12468
12489
  }),
12469
12490
  (canPinLeft || canPinRight) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Menu.Separator, {})
12470
12491
  ] }),
12492
+ canMove && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MoveColumnSubmenu, {
12493
+ field: column.field,
12494
+ columnsModel
12495
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Menu.Separator, {})] }),
12471
12496
  hasInsertableFields && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
12472
12497
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InsertColumnSubmenu, {
12473
12498
  label: "Insert before",
@@ -12594,6 +12619,7 @@ const ColumnHeader = (0, mobx_react_lite.observer)(({ column, columnsModel, sort
12594
12619
  textDecoration: column.isDeprecated ? "line-through" : void 0,
12595
12620
  children: column.label
12596
12621
  }),
12622
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormulaIndicator, { column }),
12597
12623
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PinIndicator, {
12598
12624
  field: column.field,
12599
12625
  columnsModel
@@ -12795,8 +12821,12 @@ const RowActionMenuItems = ({ rowId, testIdPrefix, onSelect, onDuplicate, onDele
12795
12821
 
12796
12822
  //#endregion
12797
12823
  //#region src/table-editor/Table/ui/RowActionOverlay.tsx
12798
- const RowActionOverlay = ({ rowId, onOpen, onSelect, onDuplicate, onDelete }) => {
12824
+ const RowActionOverlay = ({ rowId, onOpen, onPick, onSelect, onDuplicate, onDelete }) => {
12799
12825
  const [isMenuOpen, setIsMenuOpen] = (0, react.useState)(false);
12826
+ const primaryAction = onPick ?? onOpen;
12827
+ const primaryIcon = onPick ? react_icons_pi.PiCheckSquare : react_icons_pi.PiArrowSquareRightLight;
12828
+ const primaryTestId = onPick ? `row-action-pick-${rowId}` : `row-action-open-${rowId}`;
12829
+ const hasMenuItems = Boolean(onOpen || onSelect || onDuplicate || onDelete);
12800
12830
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Flex, {
12801
12831
  position: "absolute",
12802
12832
  right: "8px",
@@ -12814,7 +12844,7 @@ const RowActionOverlay = ({ rowId, onOpen, onSelect, onDuplicate, onDelete }) =>
12814
12844
  boxShadow: "0px 2px 8px rgba(0,0,0,0.16)",
12815
12845
  overflow: "hidden",
12816
12846
  "data-testid": `row-action-split-${rowId}`,
12817
- children: [onOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Flex, {
12847
+ children: [primaryAction && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Flex, {
12818
12848
  alignItems: "center",
12819
12849
  justifyContent: "center",
12820
12850
  height: "100%",
@@ -12824,15 +12854,15 @@ const RowActionOverlay = ({ rowId, onOpen, onSelect, onDuplicate, onDelete }) =>
12824
12854
  _hover: { bg: "gray.100" },
12825
12855
  onClick: (e) => {
12826
12856
  e.stopPropagation();
12827
- onOpen(rowId);
12857
+ primaryAction(rowId);
12828
12858
  },
12829
- "data-testid": `row-action-open-${rowId}`,
12859
+ "data-testid": primaryTestId,
12830
12860
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_chakra_ui_react.Icon, {
12831
12861
  boxSize: "20px",
12832
12862
  color: "gray.400",
12833
- as: react_icons_pi.PiArrowSquareRightLight
12863
+ as: primaryIcon
12834
12864
  })
12835
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Menu.Root, {
12865
+ }), hasMenuItems && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_chakra_ui_react.Menu.Root, {
12836
12866
  lazyMount: true,
12837
12867
  unmountOnExit: true,
12838
12868
  onOpenChange: ({ open }) => setIsMenuOpen(open),
@@ -12846,7 +12876,7 @@ const RowActionOverlay = ({ rowId, onOpen, onSelect, onDuplicate, onDelete }) =>
12846
12876
  width: "18px",
12847
12877
  minWidth: "18px",
12848
12878
  cursor: "pointer",
12849
- borderLeft: onOpen ? "1px solid" : void 0,
12879
+ borderLeft: primaryAction ? "1px solid" : void 0,
12850
12880
  borderColor: "gray.100",
12851
12881
  _hover: { bg: "gray.100" },
12852
12882
  onClick: (e) => e.stopPropagation(),
@@ -12972,8 +13002,8 @@ function getCellBoxShadow(isSticky, side) {
12972
13002
  if (!isSticky) return BOTTOM_BORDER_SHADOW;
12973
13003
  return `${BOTTOM_BORDER_SHADOW}, ${side === "left" ? `inset -1px 0 0 0 ${CELL_BORDER_COLOR}` : `inset 1px 0 0 0 ${CELL_BORDER_COLOR}`}`;
12974
13004
  }
12975
- const DataRow = (0, mobx_react_lite.observer)(({ row, columnsModel, showSelection, onSearchForeignKey, onUploadFile, onOpenFile, onOpenRow, onSelectRow, onDuplicateRow, onDeleteRow }) => {
12976
- const hasRowActions = Boolean(onOpenRow || onSelectRow || onDuplicateRow || onDeleteRow);
13005
+ const DataRow = (0, mobx_react_lite.observer)(({ row, columnsModel, showSelection, onSearchForeignKey, onUploadFile, onOpenFile, onOpenRow, onPickRow, onSelectRow, onDuplicateRow, onDeleteRow }) => {
13006
+ const hasRowActions = Boolean(onOpenRow || onPickRow || onSelectRow || onDuplicateRow || onDeleteRow);
12977
13007
  const selectionWidth = showSelection ? SELECTION_COLUMN_WIDTH : 0;
12978
13008
  const addColumnStickyRight = columnsModel.hasHiddenColumns;
12979
13009
  const addColOffset = addColumnStickyRight ? ADD_COLUMN_BUTTON_WIDTH : 0;
@@ -13020,6 +13050,7 @@ const DataRow = (0, mobx_react_lite.observer)(({ row, columnsModel, showSelectio
13020
13050
  }), showOverlay && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowActionOverlay, {
13021
13051
  rowId: row.rowId,
13022
13052
  onOpen: onOpenRow,
13053
+ onPick: onPickRow,
13023
13054
  onSelect: onSelectRow,
13024
13055
  onDuplicate: onDuplicateRow,
13025
13056
  onDelete: onDeleteRow
@@ -13310,7 +13341,7 @@ const baseComponents = {
13310
13341
  Table: TableComponent,
13311
13342
  TableRow: TableRowComponent
13312
13343
  };
13313
- 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 }) => {
13344
+ const TableWidget = (0, mobx_react_lite.observer)(({ rows, columnsModel, cellFSM, selection, sortModel, filterModel, onSearchForeignKey, onUploadFile, onOpenFile, onOpenRow, onPickRow, onDeleteSelected, onDuplicateSelected, onDeleteRow, onDuplicateRow, onCopyPath, onEndReached, isLoadingMore, useWindowScroll: useWindowScrollProp }) => {
13314
13345
  const showSelection = selection.isSelectionMode;
13315
13346
  const allRowIds = rows.map((r) => r.rowId);
13316
13347
  const [deleteConfirm, setDeleteConfirm] = (0, react.useState)(null);
@@ -13437,6 +13468,7 @@ const TableWidget = (0, mobx_react_lite.observer)(({ rows, columnsModel, cellFSM
13437
13468
  onUploadFile,
13438
13469
  onOpenFile,
13439
13470
  onOpenRow,
13471
+ onPickRow,
13440
13472
  onSelectRow: canSelect ? handleSelectRow : void 0,
13441
13473
  onDuplicateRow: canDuplicateRow ? onDuplicateRow : void 0,
13442
13474
  onDeleteRow: canDeleteRow ? handleDeleteRowRequest : void 0
@@ -13447,6 +13479,7 @@ const TableWidget = (0, mobx_react_lite.observer)(({ rows, columnsModel, cellFSM
13447
13479
  onUploadFile,
13448
13480
  onOpenFile,
13449
13481
  onOpenRow,
13482
+ onPickRow,
13450
13483
  canSelect,
13451
13484
  canDeleteRow,
13452
13485
  canDuplicateRow,
@@ -13755,6 +13788,7 @@ var TableEditorCore = class {
13755
13788
  this._dataSource = dataSource;
13756
13789
  this._tableId = options.tableId;
13757
13790
  this._pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
13791
+ this._readonly = options.readonly ?? false;
13758
13792
  this._breadcrumbs = options.breadcrumbs ?? [];
13759
13793
  this._callbacks = options.callbacks ?? {};
13760
13794
  this.columns = new ColumnsModel();
@@ -13851,8 +13885,8 @@ var TableEditorCore = class {
13851
13885
  const meta = await this._dataSource.fetchMetadata();
13852
13886
  (0, mobx.runInAction)(() => {
13853
13887
  this._schemaContext.init(meta.dataSchema, meta.refSchemas);
13854
- this._readonly = meta.readonly;
13855
- this.viewBadge.setCanSave(!meta.readonly);
13888
+ this._readonly = this._readonly || meta.readonly;
13889
+ this.viewBadge.setCanSave(!this._readonly);
13856
13890
  });
13857
13891
  this.columns.init(this._schemaContext.allColumns);
13858
13892
  this.sorts.init(this._schemaContext.sortableFields);
@@ -13923,7 +13957,7 @@ var TableEditorCore = class {
13923
13957
  });
13924
13958
  const systemValuesMap = /* @__PURE__ */ new Map();
13925
13959
  for (const raw of rawRows) systemValuesMap.set(raw.rowId, this._buildSystemValues(raw));
13926
- 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)));
13960
+ 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), this._readonly));
13927
13961
  }
13928
13962
  _buildSystemValues(rawRow) {
13929
13963
  return {
@@ -14277,6 +14311,7 @@ const TableEditor = (0, mobx_react_lite.observer)(({ viewModel, useWindowScroll
14277
14311
  isLoadingMore: viewModel.isLoadingMore,
14278
14312
  onEndReached: viewModel.loadMore,
14279
14313
  onOpenRow: callbacks.onOpenRow,
14314
+ onPickRow: callbacks.onPickRow,
14280
14315
  onDeleteRow: isReadonly ? void 0 : (id) => viewModel.deleteRows([id]),
14281
14316
  onDuplicateRow: isReadonly ? void 0 : callbacks.onDuplicateRow,
14282
14317
  onDeleteSelected: isReadonly ? void 0 : (ids) => viewModel.deleteRows(ids),