@singularsystems/neo-react 1.0.15 → 1.0.17

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.
@@ -50,8 +50,8 @@ var Button = /** @class */ (function (_super) {
50
50
  };
51
51
  Button.prototype.afterRender = function () {
52
52
  if (this.btnGroupDom.current && this.menuDom.current) {
53
- this.menuDom.current.style.position = "fixed"; // override bootstrap style.
54
- this.positionHelper = new Components.FixedPositionHelper(this.btnGroupDom.current, this.menuDom.current, this.props.menuAlignment === "right");
53
+ this.positionHelper = new Components.FixedPositionHelper(this.btnGroupDom.current, this.menuDom.current);
54
+ this.positionHelper.positionPopup(this.props.menuAlignment === "right");
55
55
  }
56
56
  };
57
57
  Button.prototype.componentWillUnmount = function () {
@@ -25,6 +25,12 @@ interface IAutoCompleteCommonProps<T> extends ICommonEditorProps, Props<IOption,
25
25
  * Function used to filter items out of the datasource.
26
26
  * **Note:** this will only apply to the `items` prop. */
27
27
  filterPredicate?: (item: T) => boolean;
28
+ /**
29
+ * Function used to filter items based on what the user has typed.
30
+ * Applies after `filterPredicate`, and is ignored if `itemSource` is specified.
31
+ * Use if you are displaying multiple columns and need to match on any of the columns.
32
+ */
33
+ inputFilter?: (input: string, item: T) => boolean;
28
34
  /**
29
35
  * Property of an item to use as the description.
30
36
  *
@@ -128,7 +128,7 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
128
128
  AutoCompleteDropDown.prototype.render = function () {
129
129
  var _this = this;
130
130
  var _a, _b, _c, _d, _e, _f, _g, _h;
131
- var _j = this.props, bind = _j.bind, bindDisplay = _j.bindDisplay, bindItems = _j.bindItems, bindIds = _j.bindIds, itemSource = _j.itemSource, items = _j.items, displayMember = _j.displayMember, valueMember = _j.valueMember, descriptionMember = _j.descriptionMember, option = _j.option, noRecordsPrompt = _j.noRecordsPrompt, typeToSearchPrompt = _j.typeToSearchPrompt, delayMs = _j.delayMs, minCharacters = _j.minCharacters, onItemSelected = _j.onItemSelected, fieldInfoLimit = _j.fieldInfoLimit, otherProps = __rest(_j, ["bind", "bindDisplay", "bindItems", "bindIds", "itemSource", "items", "displayMember", "valueMember", "descriptionMember", "option", "noRecordsPrompt", "typeToSearchPrompt", "delayMs", "minCharacters", "onItemSelected", "fieldInfoLimit"]);
131
+ var _j = this.props, bind = _j.bind, bindDisplay = _j.bindDisplay, bindItems = _j.bindItems, bindIds = _j.bindIds, itemSource = _j.itemSource, items = _j.items, displayMember = _j.displayMember, valueMember = _j.valueMember, descriptionMember = _j.descriptionMember, option = _j.option, noRecordsPrompt = _j.noRecordsPrompt, typeToSearchPrompt = _j.typeToSearchPrompt, delayMs = _j.delayMs, minCharacters = _j.minCharacters, onItemSelected = _j.onItemSelected, fieldInfoLimit = _j.fieldInfoLimit, filterPredicate = _j.filterPredicate, inputFilter = _j.inputFilter, otherProps = __rest(_j, ["bind", "bindDisplay", "bindItems", "bindIds", "itemSource", "items", "displayMember", "valueMember", "descriptionMember", "option", "noRecordsPrompt", "typeToSearchPrompt", "delayMs", "minCharacters", "onItemSelected", "fieldInfoLimit", "filterPredicate", "inputFilter"]);
132
132
  this.setBindProperty();
133
133
  var splitProps = BindPropsHelper.splitCommonEditorProps(otherProps);
134
134
  var nativeProps = splitProps.rest;
@@ -148,13 +148,13 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
148
148
  if (this.props.items) {
149
149
  var items_1 = this.getItems(this.props.items);
150
150
  defaultItems = this.mapTArrayToOptions(items_1);
151
- if (this.props.filterPredicate !== undefined) {
151
+ if (filterPredicate !== undefined) {
152
152
  if (bindValue) {
153
153
  // Dont filter out the current selected item even if excluded by the filter.
154
- defaultItems = defaultItems.filter(function (option) { return bindValue.value === option.value || _this.props.filterPredicate(option.item); });
154
+ defaultItems = defaultItems.filter(function (option) { return bindValue.value === option.value || filterPredicate(option.item); });
155
155
  }
156
156
  else {
157
- defaultItems = defaultItems.filter(function (option) { return _this.props.filterPredicate(option.item); });
157
+ defaultItems = defaultItems.filter(function (option) { return filterPredicate(option.item); });
158
158
  }
159
159
  }
160
160
  if (descriptionMember === undefined && items_1.length > 0) {
@@ -221,6 +221,11 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
221
221
  userOnBlur(e);
222
222
  }
223
223
  };
224
+ if (this.props.inputFilter && !this.props.itemSource) {
225
+ nativeProps.filterOption = function (option, input) {
226
+ return !input || _this.props.inputFilter(input, option.data.item);
227
+ };
228
+ }
224
229
  if (this.props.itemSource !== undefined) {
225
230
  return Tooltip.fromDataAttributes(InputHelpers.surroundWithInputGroup(React.createElement(this.asyncSelectImplementation, __assign({}, nativeProps, { placeholder: (_g = this.props.placeholder) !== null && _g !== void 0 ? _g : "", classNamePrefix: "react-select-bs", defaultOptions: defaultItems, loadOptions: this.onKeyPress, isMulti: bindItems !== undefined, noOptionsMessage: function (c) { var _a, _b; return c.inputValue && c.inputValue.length >= (minCharacters !== null && minCharacters !== void 0 ? minCharacters : 1) ? ((_a = _this.props.noRecordsPrompt) !== null && _a !== void 0 ? _a : "No records found") : ((_b = _this.props.typeToSearchPrompt) !== null && _b !== void 0 ? _b : "Type to search..."); }, value: value, onChange: this.onItemSelected, components: components })), splitProps.editor, { disabled: nativeProps.isDisabled }), nativeProps);
226
231
  }
@@ -58,7 +58,10 @@ export interface IGridProps<T> extends Omit<React.HTMLProps<HTMLTableElement>, "
58
58
  * This is the default unless overridden in Misc.Settings.grid
59
59
  */
60
60
  isBordered?: boolean;
61
- /** When specified, every alternating row will receive a slightly darker background color. */
61
+ /**
62
+ * When specified, every alternating row will receive a slightly darker background color.
63
+ * Note, this will not work correctly if your grid has child rows. See the neo react demo pages for a solution.
64
+ */
62
65
  isStriped?: boolean;
63
66
  /** When specified, the table will have no cell borders, and the table will have no main border. */
64
67
  isBorderless?: boolean;
@@ -79,6 +82,11 @@ export interface IGridProps<T> extends Omit<React.HTMLProps<HTMLTableElement>, "
79
82
  * Note: if your app has a fixed header, you need to specify this in the global neo settings.
80
83
  */
81
84
  stickyHeader?: boolean;
85
+ /**
86
+ * When true, the index parameter passed to the render function will be set.
87
+ * This has performance implications, as each row needs to be re-rendered on sorting or insertion of items.
88
+ */
89
+ enableRowIndexes?: boolean;
82
90
  }
83
91
  export default class Grid<T> extends React.Component<IGridProps<T>> {
84
92
  static contextType: React.Context<import("../../React/DataView").DataViewContextParams<unknown>>;
@@ -45,7 +45,7 @@ var Grid = /** @class */ (function (_super) {
45
45
  };
46
46
  Grid.prototype.render = function () {
47
47
  var _this = this;
48
- var _a = this.props, items = _a.items, showAddButton = _a.showAddButton, addButton = _a.addButton, filterPredicate = _a.filterPredicate, keyProperty = _a.keyProperty, allowSort = _a.allowSort, expandAll = _a.expandAll, collapseAll = _a.collapseAll, initialSort = _a.initialSort, initialSortAscending = _a.initialSortAscending, isBordered = _a.isBordered, isStriped = _a.isStriped, isBorderless = _a.isBorderless, showHover = _a.showHover, isSmallTable = _a.isSmallTable, footerContent = _a.footerContent, footerButtons = _a.footerButtons, stickyHeader = _a.stickyHeader, domProps = __rest(_a, ["items", "showAddButton", "addButton", "filterPredicate", "keyProperty", "allowSort", "expandAll", "collapseAll", "initialSort", "initialSortAscending", "isBordered", "isStriped", "isBorderless", "showHover", "isSmallTable", "footerContent", "footerButtons", "stickyHeader"]);
48
+ var _a = this.props, items = _a.items, showAddButton = _a.showAddButton, addButton = _a.addButton, filterPredicate = _a.filterPredicate, keyProperty = _a.keyProperty, allowSort = _a.allowSort, expandAll = _a.expandAll, collapseAll = _a.collapseAll, initialSort = _a.initialSort, initialSortAscending = _a.initialSortAscending, isBordered = _a.isBordered, isStriped = _a.isStriped, isBorderless = _a.isBorderless, showHover = _a.showHover, isSmallTable = _a.isSmallTable, footerContent = _a.footerContent, footerButtons = _a.footerButtons, stickyHeader = _a.stickyHeader, enableRowIndexes = _a.enableRowIndexes, domProps = __rest(_a, ["items", "showAddButton", "addButton", "filterPredicate", "keyProperty", "allowSort", "expandAll", "collapseAll", "initialSort", "initialSortAscending", "isBordered", "isStriped", "isBorderless", "showHover", "isSmallTable", "footerContent", "footerButtons", "stickyHeader", "enableRowIndexes"]);
49
49
  if (expandAll === true) {
50
50
  this.gridContext.expandAll = true;
51
51
  }
@@ -101,12 +101,12 @@ var Grid = /** @class */ (function (_super) {
101
101
  }
102
102
  return (React.createElement("table", __assign({}, domProps, { className: className, ref: this.domRef }),
103
103
  React.createElement(GridContext.Provider, { value: this.gridContext },
104
- React.createElement(TableSection, { type: "header" }, this.headerItem && this.props.children(this.headerItem, this.headerItem.meta, true)),
105
- React.createElement(TableSection, { type: "body" }, this.gridContext.dataView.getItems().map(function (item) { return (React.createElement(ItemGroup, { item: item, key: keyProperty ? item[keyProperty] : item.entityIdentifier, template: _this.props.children })); })),
104
+ React.createElement(TableSection, { type: "header" }, this.headerItem && this.props.children(this.headerItem, this.headerItem.meta, true, -1)),
105
+ React.createElement(TableSection, { type: "body" }, this.gridContext.dataView.getItems().map(function (item, index) { return (React.createElement(ItemGroup, { index: enableRowIndexes ? index : 0, item: item, key: keyProperty ? item[keyProperty] : item.entityIdentifier, template: _this.props.children })); })),
106
106
  React.createElement(Observer, null, function () {
107
107
  return React.createElement(React.Fragment, null, (showAddButton || addButton || footerButtons || _this.gridContext.hasSummaries || footerContent !== undefined) &&
108
108
  React.createElement(TableSection, { type: "footer" },
109
- _this.gridContext.hasSummaries && _this.headerItem && _this.props.children(_this.headerItem, _this.headerItem.meta, true),
109
+ _this.gridContext.hasSummaries && _this.headerItem && _this.props.children(_this.headerItem, _this.headerItem.meta, true, -1),
110
110
  footerContent !== undefined && footerContent(_this.gridContext.dataView.getItems()),
111
111
  (showAddButton || addButton || footerButtons) &&
112
112
  React.createElement(AddButtonRow, { addButton: addButton, showAddButton: showAddButton, additionalContent: footerButtons })));
@@ -207,8 +207,7 @@ var ItemGroup = /** @class */ (function (_super) {
207
207
  }
208
208
  ItemGroup.prototype.render = function () {
209
209
  this.context.currentItem = this.props.item;
210
- //this.context.inFirstRowInGroup = true;
211
- return (this.props.template(this.props.item, this.props.item.meta, false));
210
+ return this.props.template(this.props.item, this.props.item.meta, false, this.props.index);
212
211
  };
213
212
  ItemGroup.contextType = GridContext;
214
213
  ItemGroup = __decorate([
@@ -13,6 +13,8 @@ interface IRowProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLTab
13
13
  * Handler for click events on header or footer rows. The section is passed as the first argument.
14
14
  */
15
15
  onHeaderClick?: (section: GridSection, e: React.MouseEvent<HTMLTableRowElement, MouseEvent>) => void;
16
+ /** Class name that will not be applied to headers. */
17
+ bodyClassName?: string;
16
18
  }
17
19
  interface IState {
18
20
  isExpanded: boolean;
@@ -53,7 +53,7 @@ var Row = /** @class */ (function (_super) {
53
53
  Row.prototype.render = function () {
54
54
  var _this = this;
55
55
  _super.prototype.onRender.call(this);
56
- var _a = this.props, showInHeader = _a.showInHeader, onHeaderClick = _a.onHeaderClick, domProps = __rest(_a, ["showInHeader", "onHeaderClick"]);
56
+ var _a = this.props, showInHeader = _a.showInHeader, onHeaderClick = _a.onHeaderClick, bodyClassName = _a.bodyClassName, domProps = __rest(_a, ["showInHeader", "onHeaderClick", "bodyClassName"]);
57
57
  var inBody = this.section === "body";
58
58
  if (this.stateObserver)
59
59
  this.stateObserver.observe();
@@ -61,11 +61,16 @@ var Row = /** @class */ (function (_super) {
61
61
  throw "NeoGrid.Row cannot be used outside of a NeoGrid.";
62
62
  }
63
63
  var expandButtonOptions = __assign({}, Misc.Settings.grid.expandButton);
64
- if (inBody && this.expandProperty) {
65
- expandButtonOptions.onClick = this.onExpandClick;
66
- expandButtonOptions.icon = this.expandProperty.value ? Misc.Settings.icons.expanded : Misc.Settings.icons.collapsed;
64
+ if (inBody) {
65
+ if (this.expandProperty) {
66
+ expandButtonOptions.onClick = this.onExpandClick;
67
+ expandButtonOptions.icon = this.expandProperty.value ? Misc.Settings.icons.expanded : Misc.Settings.icons.collapsed;
68
+ }
69
+ if (bodyClassName) {
70
+ domProps.className = Utils.joinWithSpaces(domProps.className, bodyClassName);
71
+ }
67
72
  }
68
- if (!inBody) {
73
+ else {
69
74
  if (onHeaderClick) {
70
75
  domProps.onClick = function (e) { return onHeaderClick(_this.section, e); };
71
76
  }
@@ -11,6 +11,8 @@ export declare class StickyTableHeader {
11
11
  * @param fixedElement A fixed element at the top of the screen to use as an anchor, rather than the top of the screen itself.
12
12
  */
13
13
  constructor(table: HTMLTableElement, fixedElement?: HTMLElement | undefined);
14
+ private domObserver;
15
+ private originalWidths;
14
16
  /** Parent element that may scroll, requiring a position update. */
15
17
  private scrollElement?;
16
18
  /** Fixed height parent element that the grid scrolls within. */
@@ -21,7 +23,9 @@ export declare class StickyTableHeader {
21
23
  private handleScroll;
22
24
  private removeCopy;
23
25
  private onPossibleContentChange;
24
- private setColumnWidths;
26
+ private syncColumnWidths;
27
+ private storeColumnWidths;
28
+ private restoreColumnWidths;
25
29
  dispose(): void;
26
30
  /**
27
31
  * Returns the first parent element that has a scrollbar.
@@ -9,8 +9,13 @@ var StickyTableHeader = /** @class */ (function () {
9
9
  * @param fixedElement A fixed element at the top of the screen to use as an anchor, rather than the top of the screen itself.
10
10
  */
11
11
  function StickyTableHeader(table, fixedElement) {
12
+ var _this = this;
12
13
  this.table = table;
13
14
  this.fixedElement = fixedElement;
15
+ this.domObserver = new MutationObserver(function (mutations) {
16
+ _this.syncColumnWidths();
17
+ });
18
+ this.originalWidths = [];
14
19
  this.headerHeight = 0;
15
20
  if (!table.tHead) {
16
21
  throw Error("Table must have a header section.");
@@ -46,28 +51,32 @@ var StickyTableHeader = /** @class */ (function () {
46
51
  if (!this.headerCopy) {
47
52
  // Clone the header, and re-insert it into the table. The table will then have 2 headers.
48
53
  // The original headers position is set to "fixed" which makes it float.
49
- // The column widths of the floating header then need to be synchronised with the header that is still inside the table layout.
54
+ // The original header is floated instead of the clone because the react events are attached to these cells, and they are the cells that are visible. Without this, sorting wouldn't work.
55
+ // The column widths of the floating header then need to be synchronized with the header that is still inside the table layout.
50
56
  this.headerHeight = this.header.getBoundingClientRect().height;
51
57
  this.headerCopy = this.header.cloneNode(true);
52
58
  this.table.insertBefore(this.headerCopy, this.header);
53
59
  this.header.classList.add("neo-grid-fixed-header");
54
- this.setColumnWidths();
60
+ this.storeColumnWidths();
61
+ this.syncColumnWidths();
62
+ this.domObserver.observe(this.header, { childList: true, subtree: true, attributes: false });
55
63
  }
56
64
  this.header.style.left = tableBounds.left + "px";
57
65
  this.header.style.top = topBoundary + "px";
58
66
  if (this.scrollElement) {
59
67
  var left = this.scrollElement.scrollLeft;
60
- var width = this.scrollElement.clientWidth + left;
68
+ var width = this.scrollElement.clientWidth + left - 1;
61
69
  this.header.style.clipPath = "rect(0px ".concat(width, "px 100% ").concat(left, "px)");
62
70
  }
63
71
  }
64
72
  else {
65
- this.setColumnWidths(true);
66
73
  this.removeCopy();
67
74
  }
68
75
  };
69
76
  StickyTableHeader.prototype.removeCopy = function () {
70
77
  if (this.headerCopy) {
78
+ this.domObserver.disconnect();
79
+ this.restoreColumnWidths();
71
80
  this.header.classList.remove("neo-grid-fixed-header");
72
81
  this.header.style.clipPath = "";
73
82
  this.table.removeChild(this.headerCopy);
@@ -76,20 +85,40 @@ var StickyTableHeader = /** @class */ (function () {
76
85
  };
77
86
  StickyTableHeader.prototype.onPossibleContentChange = function () {
78
87
  var _this = this;
79
- setTimeout(function () { return _this.setColumnWidths(); }, 0);
88
+ setTimeout(function () { return _this.syncColumnWidths(); }, 0);
80
89
  };
81
- StickyTableHeader.prototype.setColumnWidths = function (isRemoving) {
82
- if (isRemoving === void 0) { isRemoving = false; }
90
+ StickyTableHeader.prototype.syncColumnWidths = function () {
83
91
  if (this.headerCopy) {
84
92
  for (var i = 0; i < this.header.rows.length; i++) {
85
93
  var originalRow = this.header.rows[i], copyRow = this.headerCopy.rows[i];
94
+ if (originalRow.cells.length !== copyRow.cells.length) {
95
+ this.removeCopy();
96
+ this.handleScroll();
97
+ return;
98
+ }
86
99
  for (var j = 0; j < originalRow.cells.length; j++) {
87
100
  var originalCell = originalRow.cells[j], copyCell = copyRow.cells[j];
88
- originalCell.style.width = isRemoving ? copyCell.style.width : window.getComputedStyle(copyCell).width;
101
+ originalCell.style.width = window.getComputedStyle(copyCell).width;
89
102
  }
90
103
  }
91
104
  }
92
105
  };
106
+ StickyTableHeader.prototype.storeColumnWidths = function () {
107
+ this.originalWidths = [];
108
+ for (var i = 0; i < this.header.rows.length; i++) {
109
+ var row = this.header.rows[i];
110
+ for (var j = 0; j < row.cells.length; j++) {
111
+ var cell = row.cells[j];
112
+ this.originalWidths.push({ cell: cell, width: cell.style.width });
113
+ }
114
+ }
115
+ };
116
+ StickyTableHeader.prototype.restoreColumnWidths = function () {
117
+ for (var _i = 0, _a = this.originalWidths; _i < _a.length; _i++) {
118
+ var cellRef = _a[_i];
119
+ cellRef.cell.style.width = cellRef.width;
120
+ }
121
+ };
93
122
  StickyTableHeader.prototype.dispose = function () {
94
123
  document.removeEventListener("scroll", this.handleScroll);
95
124
  if (this.scrollElement) {
@@ -11,7 +11,7 @@ import PropertyInfo from '@singularsystems/neo-core/dist/Model/PropertyInfo';
11
11
  import { IComponentBase } from './ComponentBase';
12
12
  import FileInput, { IFileEditorProps } from './FileManager/FileInput';
13
13
  import { FormContextParams } from './FormContext';
14
- export type ItemCallback<T> = (item: T, meta: TransformMetaType<T>, isHeader: boolean) => React.ReactNode;
14
+ export type ItemCallback<T> = (item: T, meta: TransformMetaType<T>, isHeader: boolean, index: number) => React.ReactNode;
15
15
  export declare enum ControlType {
16
16
  Date = 0,
17
17
  Number = 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@singularsystems/neo-react",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "React application logic and components for the Neo client library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -31,7 +31,7 @@
31
31
  "typescript": "5.4.2"
32
32
  },
33
33
  "dependencies": {
34
- "@singularsystems/neo-core": "1.0.12",
34
+ "@singularsystems/neo-core": "1.0.13",
35
35
  "@types/rc-slider": "^8.6.5",
36
36
  "@types/react-color": "^3.0.1",
37
37
  "axios": "1.6.7",
@@ -49,7 +49,7 @@
49
49
  "tslib": "2.5.0"
50
50
  },
51
51
  "peerDependencies": {
52
- "@singularsystems/neo-core": ">=1.0.12",
52
+ "@singularsystems/neo-core": ">=1.0.13",
53
53
  "axios": ">=1.6.2",
54
54
  "inversify": ">=5.0.1",
55
55
  "mobx": ">=6.9.0",