@singularsystems/neo-react 0.10.62 → 0.10.64

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,12 +50,12 @@ var Button = /** @class */ (function (_super) {
50
50
  };
51
51
  Button.prototype.render = function () {
52
52
  var _this = this;
53
- var _a;
54
- var _b = this.props, isSubmit = _b.isSubmit, variant = _b.variant, size = _b.size, isOutline = _b.isOutline, icon = _b.icon, iconAlignment = _b.iconAlignment, text = _b.text, menuItems = _b.menuItems, splitMenu = _b.splitMenu, menuAlignment = _b.menuAlignment, buttonGroupClassName = _b.buttonGroupClassName, tooltip = _b.tooltip, task = _b.task, isBusy = _b.isBusy, pulse = _b.pulse, native = __rest(_b, ["isSubmit", "variant", "size", "isOutline", "icon", "iconAlignment", "text", "menuItems", "splitMenu", "menuAlignment", "buttonGroupClassName", "tooltip", "task", "isBusy", "pulse"]);
53
+ var _a, _b;
54
+ var _c = this.props, isSubmit = _c.isSubmit, variant = _c.variant, size = _c.size, isOutline = _c.isOutline, icon = _c.icon, iconAlignment = _c.iconAlignment, text = _c.text, menuItems = _c.menuItems, splitMenu = _c.splitMenu, menuAlignment = _c.menuAlignment, buttonGroupClassName = _c.buttonGroupClassName, tooltip = _c.tooltip, task = _c.task, isBusy = _c.isBusy, pulse = _c.pulse, customVariant = _c.customVariant, native = __rest(_c, ["isSubmit", "variant", "size", "isOutline", "icon", "iconAlignment", "text", "menuItems", "splitMenu", "menuAlignment", "buttonGroupClassName", "tooltip", "task", "isBusy", "pulse", "customVariant"]);
55
55
  native.type = isSubmit ? "submit" : "button";
56
- variant = (variant !== null && variant !== void 0 ? variant : "primary");
56
+ var variantClassName = (_a = (customVariant !== null && customVariant !== void 0 ? customVariant : variant), (_a !== null && _a !== void 0 ? _a : "primary"));
57
57
  var isDropDown = menuItems && menuItems.length;
58
- var buttonClasses = "btn btn-" + (isOutline ? "outline-" : "") + variant;
58
+ var buttonClasses = "btn btn-" + (isOutline ? "outline-" : "") + variantClassName;
59
59
  if (size)
60
60
  buttonClasses += " btn-" + size;
61
61
  if (isDropDown && !splitMenu) {
@@ -66,14 +66,14 @@ var Button = /** @class */ (function (_super) {
66
66
  if (isBusy !== undefined || task !== undefined) {
67
67
  iconStyle = { fixedWidth: true };
68
68
  }
69
- if (isBusy || ((_a = task) === null || _a === void 0 ? void 0 : _a.isBusy)) {
69
+ if (isBusy || ((_b = task) === null || _b === void 0 ? void 0 : _b.isBusy)) {
70
70
  iconStyle.fixedWidth = true;
71
71
  iconStyle.spin = true;
72
72
  icon = Misc.Settings.icons.loadingIcon;
73
73
  native.disabled = true;
74
74
  }
75
75
  if (pulse) {
76
- buttonClasses += " " + variant + "-color-pulse";
76
+ buttonClasses += " " + variantClassName + "-color-pulse";
77
77
  }
78
78
  native.className = (native.className || "") + " " + buttonClasses;
79
79
  // Button text
@@ -19,7 +19,7 @@ var Card = /** @class */ (function (_super) {
19
19
  var isCollapsible = collapsible || isExpanded !== undefined || initiallyCollapsed !== undefined;
20
20
  var expandState = (isExpanded !== null && isExpanded !== void 0 ? isExpanded : this.expandState);
21
21
  if (isCollapsible) {
22
- var expandIndicator = React.createElement("i", { onClick: function () { return expandState.value = !expandState.value; }, className: "card-expand-indicator fa fa-fw fa-angle-" + (expandState.value ? "up" : "down") });
22
+ var expandIndicator = React.createElement("i", { onClick: function (e) { return expandState.value = !expandState.value; }, className: "card-expand-indicator fa fa-fw fa-angle-" + (expandState.value ? "up" : "down") });
23
23
  if (headerElements) {
24
24
  headerElements = React.createElement(React.Fragment, null,
25
25
  headerElements,
@@ -31,6 +31,7 @@ var Card = /** @class */ (function (_super) {
31
31
  if (!expandState.value) {
32
32
  className += " collapsed";
33
33
  }
34
+ className += " card-collapsible";
34
35
  }
35
36
  var body = React.createElement("div", { className: "card-body" },
36
37
  useCustomScrollbar && React.createElement(Scrollbar, null, children),
@@ -41,11 +42,11 @@ var Card = /** @class */ (function (_super) {
41
42
  }
42
43
  return (React.createElement("div", __assign({}, domProps, { className: className }),
43
44
  (icon || title) &&
44
- React.createElement("div", { className: "card-header" },
45
+ React.createElement("div", { className: "card-header", onClick: function (e) { return expandState.value = !expandState.value; } },
45
46
  React.createElement("div", { className: "card-header-title" },
46
47
  this.iconFactory.getIconComponent(icon),
47
48
  React.createElement("span", null, title)),
48
- headerElements && React.createElement("div", { className: "card-header-right-section" }, headerElements)),
49
+ headerElements && React.createElement("div", { className: "card-header-right-section", onClick: function (e) { return e.stopPropagation(); } }, headerElements)),
49
50
  body));
50
51
  };
51
52
  Card = __decorate([
@@ -188,7 +188,7 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
188
188
  if (nativeProps.isClearable === undefined) {
189
189
  nativeProps.isClearable = true;
190
190
  }
191
- if (splitProps.editor.isDisabled || splitProps.editor.isReadOnly) {
191
+ if (BindPropsHelper.isReadOnly(splitProps.editor, this.context) || BindPropsHelper.isDisabled(splitProps.editor, this.context)) {
192
192
  nativeProps.isDisabled = true;
193
193
  }
194
194
  nativeProps.className = Utils.joinWithSpaces("react-select-bs", nativeProps.className);
@@ -73,6 +73,11 @@ export interface IGridProps<T> extends React.HTMLProps<HTMLTableElement> {
73
73
  * Set `showAddButton = false` if you don't want the default button.
74
74
  */
75
75
  footerButtons?: () => React.ReactNode;
76
+ /**
77
+ * Will cause the header to stick to the top of the screen / scroll element when scrolling.
78
+ * Note: if your app has a fixed header, you need to specify this in the global neo settings.
79
+ */
80
+ stickyHeader?: boolean;
76
81
  }
77
82
  export default class Grid<T> extends React.Component<IGridProps<T>> {
78
83
  static contextType: React.Context<import("../../React/DataView").DataViewContextParams<unknown>>;
@@ -80,7 +85,11 @@ export default class Grid<T> extends React.Component<IGridProps<T>> {
80
85
  private gridContext;
81
86
  private prevItems?;
82
87
  private headerItem;
88
+ private domRef;
89
+ private stickyHeaderHelper?;
83
90
  constructor(props: IGridProps<T>);
91
+ componentDidMount(): void;
92
+ componentWillUnmount(): void;
84
93
  render(): JSX.Element;
85
94
  private setInitialSort;
86
95
  }
@@ -4,6 +4,7 @@ import { Data, Utils, Misc } from '@singularsystems/neo-core';
4
4
  import { observer, Observer } from 'mobx-react';
5
5
  import NeoButton from '../Button';
6
6
  import { DataViewContext } from '../../React/DataView';
7
+ import { StickyTableHeader } from './StickyTableHeader';
7
8
  // Grid context
8
9
  var GridContextParams = /** @class */ (function () {
9
10
  function GridContextParams() {
@@ -27,12 +28,24 @@ var Grid = /** @class */ (function (_super) {
27
28
  function Grid(props) {
28
29
  var _this = _super.call(this, props) || this;
29
30
  _this.gridContext = new GridContextParams();
31
+ _this.domRef = React.createRef();
30
32
  _this.gridContext.section = "header";
31
33
  return _this;
32
34
  }
35
+ Grid.prototype.componentDidMount = function () {
36
+ var _a, _b;
37
+ if (this.domRef.current && (_a = this.props.stickyHeader, (_a !== null && _a !== void 0 ? _a : Misc.Settings.grid.useStickyHeaders))) {
38
+ this.stickyHeaderHelper = new StickyTableHeader(this.domRef.current, (_b = Misc.Settings.fixedAppHeaderElement) === null || _b === void 0 ? void 0 : _b.call(undefined));
39
+ }
40
+ };
41
+ Grid.prototype.componentWillUnmount = function () {
42
+ if (this.stickyHeaderHelper) {
43
+ this.stickyHeaderHelper.dispose();
44
+ }
45
+ };
33
46
  Grid.prototype.render = function () {
34
47
  var _this = this;
35
- 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, domProps = __rest(_a, ["items", "showAddButton", "addButton", "filterPredicate", "keyProperty", "allowSort", "expandAll", "collapseAll", "initialSort", "initialSortAscending", "isBordered", "isStriped", "isBorderless", "showHover", "isSmallTable", "footerContent", "footerButtons"]);
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"]);
36
49
  if (expandAll === true) {
37
50
  this.gridContext.expandAll = true;
38
51
  }
@@ -86,7 +99,7 @@ var Grid = /** @class */ (function (_super) {
86
99
  }
87
100
  this.headerItem = this.gridContext.dataView.getMetaItem();
88
101
  }
89
- return (React.createElement("table", __assign({}, domProps, { className: className }),
102
+ return (React.createElement("table", __assign({}, domProps, { className: className, ref: this.domRef }),
90
103
  React.createElement(GridContext.Provider, { value: this.gridContext },
91
104
  React.createElement(TableSection, { type: "header" }, this.headerItem && this.props.children(this.headerItem, this.headerItem.meta)),
92
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 })); })),
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Watches for when a table header scrolls out of view.
3
+ * When it does, adds a cloned header that is fixed at the top of the scrolling element.
4
+ */
5
+ export declare class StickyTableHeader {
6
+ private table;
7
+ private fixedElement?;
8
+ /**
9
+ * Creates the instance, and begins listening for scroll events.
10
+ * @param table The table to add sticky headers to.
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
+ */
13
+ constructor(table: HTMLTableElement, fixedElement?: HTMLElement | undefined);
14
+ private scrollParent?;
15
+ private header;
16
+ private headerCopy?;
17
+ private headerHeight;
18
+ private handleScroll;
19
+ private removeCopy;
20
+ private onPossibleContentChange;
21
+ private setColumnWidths;
22
+ dispose(): void;
23
+ /**
24
+ * Returns the first parent element that has a scrollbar.
25
+ */
26
+ private static getParentScrollElement;
27
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Watches for when a table header scrolls out of view.
3
+ * When it does, adds a cloned header that is fixed at the top of the scrolling element.
4
+ */
5
+ var StickyTableHeader = /** @class */ (function () {
6
+ /**
7
+ * Creates the instance, and begins listening for scroll events.
8
+ * @param table The table to add sticky headers to.
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
+ */
11
+ function StickyTableHeader(table, fixedElement) {
12
+ this.table = table;
13
+ this.fixedElement = fixedElement;
14
+ this.headerHeight = 0;
15
+ if (!table.tHead) {
16
+ throw Error("Table must have a header section.");
17
+ }
18
+ this.header = table.tHead;
19
+ this.scrollParent = StickyTableHeader.getParentScrollElement(table);
20
+ this.handleScroll = this.handleScroll.bind(this);
21
+ this.onPossibleContentChange = this.onPossibleContentChange.bind(this);
22
+ document.addEventListener("scroll", this.handleScroll);
23
+ if (this.scrollParent) {
24
+ this.scrollParent.addEventListener("scroll", this.handleScroll);
25
+ }
26
+ table.addEventListener("keydown", this.onPossibleContentChange);
27
+ table.addEventListener("mouseup", this.onPossibleContentChange);
28
+ }
29
+ StickyTableHeader.prototype.handleScroll = function () {
30
+ // If the grid is in an element with a scrollbar, then the header should stick to the top of that element,
31
+ // otherwise if there is a fixed (non scrolling) element at the top of the page, then the headers must appear below that.
32
+ // otherwise the headers will stick to the top of the screen.
33
+ var fixedElement = null;
34
+ if (!this.scrollParent && this.fixedElement && getComputedStyle(this.fixedElement).position === "fixed") {
35
+ fixedElement = this.fixedElement;
36
+ }
37
+ var topBoundary = this.scrollParent ? this.scrollParent.getBoundingClientRect().top : (fixedElement ? fixedElement.getBoundingClientRect().bottom : 0);
38
+ var tableBounds = this.table.getBoundingClientRect();
39
+ var tbodyBounds = this.table.tBodies[this.table.tBodies.length - 1].getBoundingClientRect();
40
+ if (tableBounds.top < topBoundary && tbodyBounds.bottom - this.headerHeight > topBoundary) {
41
+ if (!this.headerCopy) {
42
+ // Clone the header, and re-insert it into the table. The table will then have 2 headers.
43
+ // The original headers position is set to "fixed" which makes it float.
44
+ // The column widths of the floating header then need to be synchronised with the header that is still inside the table layout.
45
+ this.headerHeight = this.header.getBoundingClientRect().height;
46
+ this.headerCopy = this.header.cloneNode(true);
47
+ this.table.insertBefore(this.headerCopy, this.header);
48
+ this.header.classList.add("neo-grid-fixed-header");
49
+ this.setColumnWidths();
50
+ }
51
+ this.header.style.left = tableBounds.left + "px";
52
+ this.header.style.top = topBoundary + "px";
53
+ }
54
+ else {
55
+ this.setColumnWidths(true);
56
+ this.removeCopy();
57
+ }
58
+ };
59
+ StickyTableHeader.prototype.removeCopy = function () {
60
+ if (this.headerCopy) {
61
+ this.header.classList.remove("neo-grid-fixed-header");
62
+ this.table.removeChild(this.headerCopy);
63
+ this.headerCopy = undefined;
64
+ }
65
+ };
66
+ StickyTableHeader.prototype.onPossibleContentChange = function () {
67
+ var _this = this;
68
+ setTimeout(function () { return _this.setColumnWidths(); }, 0);
69
+ };
70
+ StickyTableHeader.prototype.setColumnWidths = function (isRemoving) {
71
+ if (isRemoving === void 0) { isRemoving = false; }
72
+ if (this.headerCopy) {
73
+ for (var i = 0; i < this.header.rows.length; i++) {
74
+ var originalRow = this.header.rows[i], copyRow = this.headerCopy.rows[i];
75
+ for (var j = 0; j < originalRow.cells.length; j++) {
76
+ var originalCell = originalRow.cells[j], copyCell = copyRow.cells[j];
77
+ originalCell.style.width = isRemoving ? copyCell.style.width : window.getComputedStyle(copyCell).width;
78
+ }
79
+ }
80
+ }
81
+ };
82
+ StickyTableHeader.prototype.dispose = function () {
83
+ document.removeEventListener("scroll", this.handleScroll);
84
+ if (this.scrollParent) {
85
+ this.scrollParent.removeEventListener("scroll", this.handleScroll);
86
+ }
87
+ this.table.removeEventListener("keydown", this.onPossibleContentChange);
88
+ this.table.removeEventListener("mouseup", this.onPossibleContentChange);
89
+ this.removeCopy();
90
+ };
91
+ /**
92
+ * Returns the first parent element that has a scrollbar.
93
+ */
94
+ StickyTableHeader.getParentScrollElement = function (element) {
95
+ var current = element;
96
+ while (current) {
97
+ if ((current.style.height || current.style.maxHeight) &&
98
+ (current.style.overflow === "scroll" || current.style.overflow === "auto" || current.style.overflowY === "scroll" || current.style.overflowY === "auto")) {
99
+ return current;
100
+ }
101
+ current = current.parentElement;
102
+ }
103
+ return undefined;
104
+ };
105
+ return StickyTableHeader;
106
+ }());
107
+ export { StickyTableHeader };
@@ -70,6 +70,10 @@ var Modal = /** @class */ (function (_super) {
70
70
  _this.visible = false;
71
71
  _this.forceUpdate();
72
72
  }
73
+ if (_this.props.afterClose) {
74
+ _this.props.afterClose();
75
+ }
76
+ ;
73
77
  _this.closeTimer = null;
74
78
  }, Misc.Settings.modalAnimationTimeMs);
75
79
  }
@@ -135,22 +135,15 @@ export declare class BindPropsHelper {
135
135
  */
136
136
  static getBindProperty<T>(allProps: IEditorContainerProps<T, any>): Model.IPropertyInstance;
137
137
  static splitCommonEditorProps<T extends ICommonEditorProps>(props: T): {
138
- editor: {
139
- isReadOnly: boolean | undefined;
140
- isDisabled: boolean | undefined;
141
- prependText: string | undefined;
142
- prepend: JSX.Element | (() => JSX.Element) | null | undefined;
143
- appendText: string | undefined;
144
- append: JSX.Element | (() => JSX.Element) | null | undefined;
145
- inputElement: React.RefObject<HTMLElement> | undefined;
146
- requiresGroup: boolean;
147
- };
138
+ editor: ICommonEditorProps;
148
139
  rest: Pick<T, Exclude<keyof T, "disabled" | "readOnly" | "isReadOnly" | "isDisabled" | "prependText" | "appendText" | "prepend" | "append" | "inputElement">>;
149
140
  };
150
141
  static normaliseContainerProps<T, TContainerDom = React.HTMLProps<T>>(component: IComponentBase, props: IEditorContainerProps<T, any> & TContainerDom, inGroup: boolean, isBootstrap?: boolean): IEditorContainerNormalisedProps<TContainerDom, any>;
151
142
  static normaliseEditorProps<T, TDom = React.HTMLProps<T>>(component: IComponentBase, props: (IEditorAndSelectorProps<T, any> & TDom) | IEditorNormalisedProps<TDom, any>): IEditorNormalisedProps<TDom, any>;
152
143
  static getPlaceholderText(placeholder: string | boolean | undefined, property: PropertyInfo): string | undefined;
153
144
  static autoSetDomProperties<TDom extends React.HTMLProps<HTMLElement>>(props: IEditorNormalisedProps<TDom, any>, formContext: FormContextParams, componentBlurFn?: (e: React.FocusEvent<HTMLElement>) => void, componentFocusFn?: (e: React.FocusEvent<HTMLElement>) => void): TDom;
145
+ static isReadOnly(editorProps: ICommonEditorProps, formContext: FormContextParams): boolean;
146
+ static isDisabled(editorProps: ICommonEditorProps, formContext: FormContextParams): boolean;
154
147
  /**
155
148
  * Intercept the user specified blur event, and run the validation onBlur logic first.
156
149
  * @param props Props specified on component.
@@ -204,12 +204,12 @@ var BindPropsHelper = /** @class */ (function () {
204
204
  if (this.config.isTestOrDevelopment) {
205
205
  domProps[BindPropsHelper.DataFieldName] = props.bind.propertyInfo.propertyName;
206
206
  }
207
- if (props.editor.isReadOnly || (formContext.allReadOnly && props.editor.isReadOnly === undefined)) {
207
+ if (this.isReadOnly(props.editor, formContext)) {
208
208
  domProps.readOnly = true;
209
209
  if (!domProps.tabIndex)
210
210
  domProps.tabIndex = -1;
211
211
  }
212
- if (props.editor.isDisabled || (formContext.allDisabled && props.editor.isDisabled === undefined)) {
212
+ if (this.isDisabled(props.editor, formContext)) {
213
213
  domProps.disabled = true;
214
214
  }
215
215
  if (props.editor.placeholder) {
@@ -226,6 +226,16 @@ var BindPropsHelper = /** @class */ (function () {
226
226
  domProps.onBlur = BindPropsHelper.blurIntercept(props.editor, domProps.onBlur, componentBlurFn);
227
227
  return domProps;
228
228
  };
229
+ BindPropsHelper.isReadOnly = function (editorProps, formContext) {
230
+ var _a;
231
+ var propsReadOnly = (_a = editorProps.isReadOnly, (_a !== null && _a !== void 0 ? _a : editorProps.readOnly));
232
+ return propsReadOnly || (formContext.allReadOnly && propsReadOnly === undefined);
233
+ };
234
+ BindPropsHelper.isDisabled = function (editorProps, formContext) {
235
+ var _a;
236
+ var propsDisabled = (_a = editorProps.isDisabled, (_a !== null && _a !== void 0 ? _a : editorProps.disabled));
237
+ return propsDisabled || (formContext.allDisabled && propsDisabled === undefined);
238
+ };
229
239
  /**
230
240
  * Intercept the user specified blur event, and run the validation onBlur logic first.
231
241
  * @param props Props specified on component.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@singularsystems/neo-react",
3
- "version": "0.10.62",
3
+ "version": "0.10.64",
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",
@@ -33,7 +33,7 @@
33
33
  "@types/rc-slider": "^8.6.5",
34
34
  "@types/react-color": "^3.0.1",
35
35
  "@types/react-select": "3.1.0",
36
- "@singularsystems/neo-core": "^0.10.63",
36
+ "@singularsystems/neo-core": "^0.10.75",
37
37
  "axios": "^0.21.1",
38
38
  "decimal.js-light": "2.5.1",
39
39
  "history": "4.10.1",
@@ -47,7 +47,7 @@
47
47
  "react-router-dom": "5.1.0"
48
48
  },
49
49
  "peerDependencies": {
50
- "@singularsystems/neo-core": ">=0.10.72",
50
+ "@singularsystems/neo-core": ">=0.10.74",
51
51
  "axios": ">=0.21.1",
52
52
  "inversify": ">=5.0.1",
53
53
  "react": ">=16.13.1",
package/styles/Card.scss CHANGED
@@ -14,11 +14,18 @@
14
14
  margin: -10px 0;
15
15
 
16
16
  .card-expand-indicator {
17
- cursor: pointer;
18
17
  margin-left: 0.5rem;
19
18
  padding-top: 0.5rem;
20
19
  padding-bottom: 0.5rem;
21
20
  }
22
21
  }
23
22
  }
23
+
24
+ &.card-collapsible {
25
+ .card-header {
26
+ &:hover {
27
+ cursor: pointer;
28
+ }
29
+ }
30
+ }
24
31
  }
package/styles/Grid.scss CHANGED
@@ -1,4 +1,9 @@
1
1
  table.neo-grid {
2
+ .neo-grid-fixed-header {
3
+ position: fixed;
4
+ z-index: 3;
5
+ }
6
+
2
7
  th {
3
8
  position: relative;
4
9