@singularsystems/neo-react 1.0.0-beta.0 → 1.0.0-beta.1
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/CHANGELOG.md +7 -7
- package/dist/ReactComponents/Button.js +5 -4
- package/dist/ReactComponents/Card.js +4 -3
- package/dist/ReactComponents/DatePicker.d.ts +1 -0
- package/dist/ReactComponents/DatePicker.js +6 -2
- package/dist/ReactComponents/DropDown/AutoCompleteDropDown.d.ts +4 -0
- package/dist/ReactComponents/DropDown/AutoCompleteDropDown.js +4 -1
- package/dist/ReactComponents/Grid/ButtonColumn.d.ts +2 -1
- package/dist/ReactComponents/Grid/ButtonColumn.js +1 -1
- package/dist/ReactComponents/Grid/Grid.d.ts +11 -3
- package/dist/ReactComponents/Grid/Grid.js +23 -8
- package/dist/ReactComponents/Grid/StickyTableHeader.d.ts +27 -0
- package/dist/ReactComponents/Grid/StickyTableHeader.js +107 -0
- package/dist/ReactComponents/Input.js +1 -1
- package/dist/ReactComponents/InputHelpers.d.ts +1 -1
- package/dist/ReactComponents/InputHelpers.js +3 -3
- package/dist/ReactComponents/Modal/Modal.js +4 -0
- package/dist/ReactComponents/NumberInput.js +14 -7
- package/dist/ReactComponents/PropTypes.d.ts +3 -10
- package/dist/ReactComponents/PropTypes.js +12 -2
- package/package.json +2 -2
- package/styles/Card.scss +8 -1
- package/styles/DatePicker.scss +11 -0
- package/styles/Grid.scss +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Version 1 of neo-react upgrades react to version 18, and mobX to version 6. Both of these are major updates and will require you to make code changes to your project.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Testing
|
|
6
|
+
|
|
7
|
+
In addition to requiring code / syntax changes, both React 18, and Mobx 6 behave differently enough to their previous versions that runtime bugs may be introduced into your project. You should only update to this version when you and the client have enough time to test the project thoroughly. If you find any issues, please report them so this document can be updated.
|
|
8
|
+
|
|
9
|
+
The project changes required are detailed below:
|
|
6
10
|
|
|
7
11
|
## Package changes:
|
|
8
12
|
|
|
@@ -85,14 +89,10 @@ makeObservable(this);
|
|
|
85
89
|
|
|
86
90
|
### Decorators on neo models
|
|
87
91
|
|
|
88
|
-
If you try use mobx decorators on any models that extend `ModelBase` or `ViewModelBase`, you will receive this error:
|
|
92
|
+
If you try use mobx decorators on any models that extend `ModelBase`, `ValueObject` or `ViewModelBase`, you will receive this error:
|
|
89
93
|
|
|
90
94
|
> [MobX] makeObservable second arg must be nullish when using decorators. Mixing @decorator syntax with annotations is not supported.
|
|
91
95
|
|
|
92
96
|
This is most likely because you have used an `@action` decorator on a method. Neo now marks all methods as actions, so you can remove the `@action` decorator.
|
|
93
97
|
|
|
94
|
-
* Search for `@action` in your project. Remove the decorator **only** if the class extends `ModelBase` or `ViewModelBase`.
|
|
95
|
-
|
|
96
|
-
## Testing
|
|
97
|
-
|
|
98
|
-
Both React 18, and Mobx 6 behave differently enough to their previous versions that new bugs are almost guaranteed. You should only update to this version when you and the client have enough time to test the project thoroughly. If you find any issues, please report them so this document can be updated.
|
|
98
|
+
* Search for `@action` in your project. Remove the decorator **only** if the class extends `ModelBase` (and its decendents e.g. `ValueObject`) or `ViewModelBase`.
|
|
@@ -50,11 +50,12 @@ var Button = /** @class */ (function (_super) {
|
|
|
50
50
|
};
|
|
51
51
|
Button.prototype.render = function () {
|
|
52
52
|
var _this = this;
|
|
53
|
-
var _a
|
|
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, customVariant = _b.customVariant, native = __rest(_b, ["isSubmit", "variant", "size", "isOutline", "icon", "iconAlignment", "text", "menuItems", "splitMenu", "menuAlignment", "buttonGroupClassName", "tooltip", "task", "isBusy", "pulse", "customVariant"]);
|
|
54
55
|
native.type = isSubmit ? "submit" : "button";
|
|
55
|
-
|
|
56
|
+
var variantClassName = ((_a = customVariant !== null && customVariant !== void 0 ? customVariant : variant) !== null && _a !== void 0 ? _a : "primary");
|
|
56
57
|
var isDropDown = menuItems && menuItems.length;
|
|
57
|
-
var buttonClasses = "btn btn-" + (isOutline ? "outline-" : "") +
|
|
58
|
+
var buttonClasses = "btn btn-" + (isOutline ? "outline-" : "") + variantClassName;
|
|
58
59
|
if (size)
|
|
59
60
|
buttonClasses += " btn-" + size;
|
|
60
61
|
if (isDropDown && !splitMenu) {
|
|
@@ -72,7 +73,7 @@ var Button = /** @class */ (function (_super) {
|
|
|
72
73
|
native.disabled = true;
|
|
73
74
|
}
|
|
74
75
|
if (pulse) {
|
|
75
|
-
buttonClasses += " ".concat(
|
|
76
|
+
buttonClasses += " ".concat(variantClassName, "-color-pulse");
|
|
76
77
|
}
|
|
77
78
|
native.className = (native.className || "") + " " + buttonClasses;
|
|
78
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-".concat(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-".concat(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([
|
|
@@ -12,6 +12,7 @@ export default class DatePicker extends ComponentBase<INeoDatePickerProps> {
|
|
|
12
12
|
private container;
|
|
13
13
|
private datePicker?;
|
|
14
14
|
private isReadOnly;
|
|
15
|
+
private inputRef;
|
|
15
16
|
constructor(props: INeoDatePickerProps);
|
|
16
17
|
componentDidMount(): void;
|
|
17
18
|
componentWillUnmount(): void;
|
|
@@ -11,6 +11,7 @@ var DatePicker = /** @class */ (function (_super) {
|
|
|
11
11
|
function DatePicker(props) {
|
|
12
12
|
var _this = _super.call(this, props) || this;
|
|
13
13
|
_this.isReadOnly = false;
|
|
14
|
+
_this.inputRef = React.createRef();
|
|
14
15
|
_this.container = React.createRef();
|
|
15
16
|
return _this;
|
|
16
17
|
}
|
|
@@ -33,6 +34,9 @@ var DatePicker = /** @class */ (function (_super) {
|
|
|
33
34
|
this.isReadOnly = !!domProps.readOnly;
|
|
34
35
|
var displayInfo = this.props.bind.validator.getDisplayState(this.context.validationDisplayMode);
|
|
35
36
|
displayInfo.applyToDom(domProps, !splitProps.inGroup);
|
|
37
|
+
if (splitProps.editor.inputElement) {
|
|
38
|
+
this.inputRef = splitProps.editor.inputElement;
|
|
39
|
+
}
|
|
36
40
|
if (this.datePicker) {
|
|
37
41
|
this.datePicker.update(splitProps.dateProps, this.props.bind, this.isReadOnly);
|
|
38
42
|
}
|
|
@@ -40,8 +44,8 @@ var DatePicker = /** @class */ (function (_super) {
|
|
|
40
44
|
// Create dependency on bound value on initial render.
|
|
41
45
|
void this.props.bind.value;
|
|
42
46
|
}
|
|
43
|
-
return (React.createElement("div", { className: "neo-datepicker-container", ref: this.container },
|
|
44
|
-
InputHelpers.surroundWithInputGroup(React.createElement("input", __assign({ type: "text" }, domProps, { ref:
|
|
47
|
+
return (React.createElement("div", { className: "neo-datepicker-container", ref: this.container }, Components.DatePicker.mustShowInput(splitProps.dateProps) &&
|
|
48
|
+
InputHelpers.surroundWithInputGroup(React.createElement("input", __assign({ type: "text" }, domProps, { ref: this.inputRef })), splitProps.editor, this.inputRef)));
|
|
45
49
|
};
|
|
46
50
|
DatePicker.contextType = FormContext;
|
|
47
51
|
DatePicker = __decorate([
|
|
@@ -21,6 +21,10 @@ interface IAutoCompleteCommonProps<T> extends ICommonEditorProps, Props<IOption,
|
|
|
21
21
|
items?: ItemSource<T>;
|
|
22
22
|
valueMember?: keyof T;
|
|
23
23
|
displayMember?: keyof T;
|
|
24
|
+
/**
|
|
25
|
+
* Function used to filter items out of the datasource.
|
|
26
|
+
* **Note:** this will only apply to the `items` prop. */
|
|
27
|
+
filterPredicate?: (item: T) => boolean;
|
|
24
28
|
/**
|
|
25
29
|
* Property of an item to use as the description.
|
|
26
30
|
*
|
|
@@ -137,6 +137,9 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
|
|
|
137
137
|
var defaultItems = undefined;
|
|
138
138
|
if (this.props.items) {
|
|
139
139
|
var items_1 = this.getItems(this.props.items);
|
|
140
|
+
if (this.props.filterPredicate !== undefined) {
|
|
141
|
+
items_1 = items_1.filter(this.props.filterPredicate);
|
|
142
|
+
}
|
|
140
143
|
defaultItems = this.mapTArrayToOptions(items_1);
|
|
141
144
|
if (descriptionMember === undefined && items_1.length > 0) {
|
|
142
145
|
if (EnumHelper.isEnumItem(items_1[0])) {
|
|
@@ -188,7 +191,7 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
|
|
|
188
191
|
if (nativeProps.isClearable === undefined) {
|
|
189
192
|
nativeProps.isClearable = true;
|
|
190
193
|
}
|
|
191
|
-
if (splitProps.editor.
|
|
194
|
+
if (BindPropsHelper.isReadOnly(splitProps.editor, this.context) || BindPropsHelper.isDisabled(splitProps.editor, this.context)) {
|
|
192
195
|
nativeProps.isDisabled = true;
|
|
193
196
|
}
|
|
194
197
|
nativeProps.className = Utils.joinWithSpaces("react-select-bs", nativeProps.className);
|
|
@@ -13,7 +13,7 @@ interface IDeleteButtonOptions extends IButtonOptions {
|
|
|
13
13
|
*/
|
|
14
14
|
onDeleteExisting?: (item: any) => Promise<boolean>;
|
|
15
15
|
}
|
|
16
|
-
export interface IButtonColumnProps extends INeoCommonColumnProps, React.HTMLProps<HTMLTableCellElement> {
|
|
16
|
+
export interface IButtonColumnProps extends INeoCommonColumnProps, Omit<React.HTMLProps<HTMLTableCellElement>, "children"> {
|
|
17
17
|
/** Item to pass to edit and delete callbacks. Defaults to the current rows item */
|
|
18
18
|
item?: any;
|
|
19
19
|
/** Text to show in the header. */
|
|
@@ -32,6 +32,7 @@ export interface IButtonColumnProps extends INeoCommonColumnProps, React.HTMLPro
|
|
|
32
32
|
viewButton?: IButtonOptions;
|
|
33
33
|
/** Where should extra content be placed relative to the pre-defined buttons? */
|
|
34
34
|
childAlignment?: "left" | "right";
|
|
35
|
+
children?: React.ReactNode | (() => React.ReactNode);
|
|
35
36
|
}
|
|
36
37
|
export declare class ButtonColumn extends ComponentBase<IButtonColumnProps> {
|
|
37
38
|
static isColumnComponent: boolean;
|
|
@@ -72,7 +72,7 @@ export var ButtonColumn = /** @class */ (function (_super) {
|
|
|
72
72
|
React.createElement("th", __assign({}, domProps), this.section === "header" && label) :
|
|
73
73
|
React.createElement("td", __assign({}, domProps),
|
|
74
74
|
childAlignment === "right" ? buttons : undefined,
|
|
75
|
-
this.props.children,
|
|
75
|
+
this.props.children instanceof Function ? this.props.children() : this.props.children,
|
|
76
76
|
childAlignment !== "right" ? buttons : undefined));
|
|
77
77
|
};
|
|
78
78
|
ButtonColumn.isColumnComponent = true;
|
|
@@ -17,7 +17,7 @@ export declare class GridContextParams<T> {
|
|
|
17
17
|
get inBody(): boolean;
|
|
18
18
|
}
|
|
19
19
|
export declare const GridContext: React.Context<GridContextParams<unknown>>;
|
|
20
|
-
interface IGridProps<T> extends Omit<React.HTMLProps<HTMLTableElement>, "children"> {
|
|
20
|
+
export interface IGridProps<T> extends Omit<React.HTMLProps<HTMLTableElement>, "children"> {
|
|
21
21
|
/**
|
|
22
22
|
* Datasource of the Grid. Usually a neo List, but can be a js array. This is not required if the grid is nested in
|
|
23
23
|
* a DataViewProvider like a Pager or DataScroller
|
|
@@ -51,6 +51,7 @@ interface IGridProps<T> extends Omit<React.HTMLProps<HTMLTableElement>, "childre
|
|
|
51
51
|
*/
|
|
52
52
|
initialSort?: keyof T | (keyof T)[];
|
|
53
53
|
initialSortAscending?: boolean | boolean[];
|
|
54
|
+
children: ItemCallback<T>;
|
|
54
55
|
/**
|
|
55
56
|
* When specified, the default bootstrap table borders (sides and cells) will be added.
|
|
56
57
|
* This is the default unless overridden in Misc.Settings.grid
|
|
@@ -72,7 +73,11 @@ interface IGridProps<T> extends Omit<React.HTMLProps<HTMLTableElement>, "childre
|
|
|
72
73
|
* Set `showAddButton = false` if you don't want the default button.
|
|
73
74
|
*/
|
|
74
75
|
footerButtons?: () => React.ReactNode;
|
|
75
|
-
|
|
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,8 +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
|
}
|
|
87
|
-
export {};
|
|
@@ -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) !== 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,16 +99,18 @@ 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 })); })),
|
|
93
|
-
React.createElement(Observer, null, function () {
|
|
94
|
-
React.createElement(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
React.createElement(Observer, null, function () {
|
|
107
|
+
return React.createElement(React.Fragment, null, (showAddButton || addButton || footerButtons || _this.gridContext.hasSummaries || footerContent !== undefined) &&
|
|
108
|
+
React.createElement(TableSection, { type: "footer" },
|
|
109
|
+
_this.gridContext.hasSummaries && _this.headerItem && _this.props.children(_this.headerItem, _this.headerItem.meta),
|
|
110
|
+
footerContent !== undefined && footerContent(_this.gridContext.dataView.getItems()),
|
|
111
|
+
(showAddButton || addButton || footerButtons) &&
|
|
112
|
+
React.createElement(AddButtonRow, { addButton: addButton, showAddButton: showAddButton, additionalContent: footerButtons })));
|
|
113
|
+
}))));
|
|
99
114
|
};
|
|
100
115
|
Grid.prototype.setInitialSort = function (initialSort, initialSortAscending) {
|
|
101
116
|
var _a;
|
|
@@ -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 };
|
|
@@ -107,7 +107,7 @@ var Input = /** @class */ (function (_super) {
|
|
|
107
107
|
splitProps.editor.append = this.props.bind.value ?
|
|
108
108
|
React.createElement(Button, __assign({ size: "sm" }, buttonOptions, { icon: !this.showPassword ? "far-eye fa-fw" : "far-eye-slash fa-fw", onClick: function () { return _this.showPassword = !_this.showPassword; } })) : null;
|
|
109
109
|
}
|
|
110
|
-
return (InputHelpers.surroundWithInputGroup(inputElement, splitProps.editor));
|
|
110
|
+
return (InputHelpers.surroundWithInputGroup(inputElement, splitProps.editor, this.domRef));
|
|
111
111
|
};
|
|
112
112
|
Input.contextType = FormContext;
|
|
113
113
|
__decorate([
|
|
@@ -2,5 +2,5 @@ import * as React from 'react';
|
|
|
2
2
|
import { ICommonEditorProps } from './PropTypes';
|
|
3
3
|
export declare class InputHelpers {
|
|
4
4
|
private static iconFactory?;
|
|
5
|
-
static surroundWithInputGroup<T extends React.ReactNode>(element: T, props: ICommonEditorProps): JSX.Element | T;
|
|
5
|
+
static surroundWithInputGroup<T extends React.ReactNode>(element: T, props: ICommonEditorProps, inputRef?: React.RefObject<HTMLElement>): JSX.Element | T;
|
|
6
6
|
}
|
|
@@ -4,7 +4,7 @@ import { NeoReactTypes } from '../Modules/Types';
|
|
|
4
4
|
var InputHelpers = /** @class */ (function () {
|
|
5
5
|
function InputHelpers() {
|
|
6
6
|
}
|
|
7
|
-
InputHelpers.surroundWithInputGroup = function (element, props) {
|
|
7
|
+
InputHelpers.surroundWithInputGroup = function (element, props, inputRef) {
|
|
8
8
|
if (!this.iconFactory) {
|
|
9
9
|
this.iconFactory = Misc.Globals.appService.get(NeoReactTypes.Components.IconFactory);
|
|
10
10
|
}
|
|
@@ -13,12 +13,12 @@ var InputHelpers = /** @class */ (function () {
|
|
|
13
13
|
(props.prependText || props.prepend) &&
|
|
14
14
|
React.createElement("div", { className: "input-group-prepend" },
|
|
15
15
|
props.prepend && (props.prepend instanceof Function ? props.prepend() : props.prepend),
|
|
16
|
-
props.prependText && React.createElement("span", { className: "input-group-text" }, this.iconFactory.textOrIcon(props.prependText))),
|
|
16
|
+
props.prependText && React.createElement("span", { className: "input-group-text", onClick: function () { var _a; return (_a = inputRef === null || inputRef === void 0 ? void 0 : inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); } }, this.iconFactory.textOrIcon(props.prependText))),
|
|
17
17
|
element,
|
|
18
18
|
(props.appendText || props.append) &&
|
|
19
19
|
React.createElement("div", { className: "input-group-append" },
|
|
20
20
|
props.append && (props.append instanceof Function ? props.append() : props.append),
|
|
21
|
-
props.appendText && React.createElement("span", { className: "input-group-text" }, this.iconFactory.textOrIcon(props.appendText)))));
|
|
21
|
+
props.appendText && React.createElement("span", { className: "input-group-text", onClick: function () { var _a; return (_a = inputRef === null || inputRef === void 0 ? void 0 : inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); } }, this.iconFactory.textOrIcon(props.appendText)))));
|
|
22
22
|
}
|
|
23
23
|
else {
|
|
24
24
|
return element;
|
|
@@ -96,8 +96,15 @@ var NumberInput = /** @class */ (function (_super) {
|
|
|
96
96
|
}
|
|
97
97
|
NumberInput.prototype.onChanged = function (e) {
|
|
98
98
|
var newValue;
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
var stringValue = e.target.value;
|
|
100
|
+
if (stringValue !== "") {
|
|
101
|
+
if (NumberUtils.allowAsDecimalSeparator) {
|
|
102
|
+
var firstIndex = stringValue.indexOf(NumberUtils.allowAsDecimalSeparator), lastIndex = stringValue.lastIndexOf(NumberUtils.allowAsDecimalSeparator);
|
|
103
|
+
if (firstIndex >= 0 && firstIndex === lastIndex) {
|
|
104
|
+
stringValue = stringValue.replace(NumberUtils.allowAsDecimalSeparator, ".");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
var testValue = stringValue;
|
|
101
108
|
if (testValue[0] === "-") {
|
|
102
109
|
testValue = testValue.substring(1);
|
|
103
110
|
}
|
|
@@ -105,18 +112,18 @@ var NumberInput = /** @class */ (function (_super) {
|
|
|
105
112
|
return;
|
|
106
113
|
}
|
|
107
114
|
}
|
|
108
|
-
if (
|
|
115
|
+
if (stringValue == "") {
|
|
109
116
|
newValue = this.props.bind.propertyInfo.allowNulls ? null : new Decimal(0);
|
|
110
117
|
}
|
|
111
118
|
else if (this.formatState.isPercent && NumberUtils.transformPercent) {
|
|
112
|
-
newValue = new Decimal(
|
|
119
|
+
newValue = new Decimal(stringValue).dividedBy(100);
|
|
113
120
|
}
|
|
114
121
|
else if (this.numericType === Misc.DataType.Float || this.numericType === Misc.DataType.Decimal) {
|
|
115
|
-
newValue = new Decimal(
|
|
122
|
+
newValue = new Decimal(stringValue);
|
|
116
123
|
}
|
|
117
124
|
else {
|
|
118
125
|
// Rounds to zero.
|
|
119
|
-
newValue = new Decimal(
|
|
126
|
+
newValue = new Decimal(stringValue).toDecimalPlaces(0, Decimal.ROUND_DOWN);
|
|
120
127
|
}
|
|
121
128
|
if (newValue === null) {
|
|
122
129
|
this.props.bind.value = newValue;
|
|
@@ -248,7 +255,7 @@ var NumberInput = /** @class */ (function (_super) {
|
|
|
248
255
|
var decimals = NumberUtils.getDecimalPlaces(this.formatState.formatString);
|
|
249
256
|
domProps.step = decimals === 0 ? "1" : "0.".concat("0".repeat(decimals - 1), "1");
|
|
250
257
|
}
|
|
251
|
-
return (InputHelpers.surroundWithInputGroup(React.createElement("input", __assign({}, domProps, { defaultValue: this.formatState.getFormattedValue(editorProps.bind.value), type: useNative ? "number" : "text", onChange: this.onChanged, ref: this.inputElement })), editorProps));
|
|
258
|
+
return (InputHelpers.surroundWithInputGroup(React.createElement("input", __assign({}, domProps, { defaultValue: this.formatState.getFormattedValue(editorProps.bind.value), type: useNative ? "number" : "text", onChange: this.onChanged, ref: this.inputElement })), editorProps, this.inputElement));
|
|
252
259
|
};
|
|
253
260
|
NumberInput.contextType = FormContext;
|
|
254
261
|
NumberInput = __decorate([
|
|
@@ -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: Omit<T, "disabled" | "readOnly" | "isReadOnly" | "isDisabled" | "prependText" | "appendText" | "prepend" | "append" | "inputElement">;
|
|
149
140
|
};
|
|
150
141
|
static normaliseContainerProps<T, TContainerDom = React.HTMLProps<T>, TData = any>(component: IComponentBase, props: IEditorContainerProps<T, TData>, inGroup: boolean, isBootstrap?: boolean): IEditorContainerNormalisedProps<TContainerDom, TData>;
|
|
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.
|
|
@@ -209,12 +209,12 @@ export var BindPropsHelper = /** @class */ (function () {
|
|
|
209
209
|
if (this.config.isTestOrDevelopment) {
|
|
210
210
|
domProps[BindPropsHelper.DataFieldName] = props.bind.propertyInfo.propertyName;
|
|
211
211
|
}
|
|
212
|
-
if (
|
|
212
|
+
if (this.isReadOnly(props.editor, formContext)) {
|
|
213
213
|
domProps.readOnly = true;
|
|
214
214
|
if (!domProps.tabIndex)
|
|
215
215
|
domProps.tabIndex = -1;
|
|
216
216
|
}
|
|
217
|
-
if (
|
|
217
|
+
if (this.isDisabled(props.editor, formContext)) {
|
|
218
218
|
domProps.disabled = true;
|
|
219
219
|
}
|
|
220
220
|
if (props.editor.placeholder) {
|
|
@@ -231,6 +231,16 @@ export var BindPropsHelper = /** @class */ (function () {
|
|
|
231
231
|
domProps.onBlur = BindPropsHelper.blurIntercept(props.editor, domProps.onBlur, componentBlurFn);
|
|
232
232
|
return domProps;
|
|
233
233
|
};
|
|
234
|
+
BindPropsHelper.isReadOnly = function (editorProps, formContext) {
|
|
235
|
+
var _a;
|
|
236
|
+
var propsReadOnly = (_a = editorProps.isReadOnly) !== null && _a !== void 0 ? _a : editorProps.readOnly;
|
|
237
|
+
return propsReadOnly || (formContext.allReadOnly && propsReadOnly === undefined);
|
|
238
|
+
};
|
|
239
|
+
BindPropsHelper.isDisabled = function (editorProps, formContext) {
|
|
240
|
+
var _a;
|
|
241
|
+
var propsDisabled = (_a = editorProps.isDisabled) !== null && _a !== void 0 ? _a : editorProps.disabled;
|
|
242
|
+
return propsDisabled || (formContext.allDisabled && propsDisabled === undefined);
|
|
243
|
+
};
|
|
234
244
|
/**
|
|
235
245
|
* Intercept the user specified blur event, and run the validation onBlur logic first.
|
|
236
246
|
* @param props Props specified on component.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@singularsystems/neo-react",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
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",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"typescript": "5.0.4"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@singularsystems/neo-core": "
|
|
32
|
+
"@singularsystems/neo-core": "1.0.0-beta.1",
|
|
33
33
|
"@types/rc-slider": "^8.6.5",
|
|
34
34
|
"@types/react-color": "^3.0.1",
|
|
35
35
|
"axios": "1.4.0",
|
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/DatePicker.scss
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
position: relative;
|
|
4
4
|
width: 100%;
|
|
5
5
|
color: initial;
|
|
6
|
+
|
|
7
|
+
&.neo-datepicker-always-show {
|
|
8
|
+
width: max-content;
|
|
9
|
+
|
|
10
|
+
input {
|
|
11
|
+
text-align: center;
|
|
12
|
+
padding: 0.125rem 0;
|
|
13
|
+
height: auto;
|
|
14
|
+
margin-bottom: 0.25rem;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
6
17
|
}
|
|
7
18
|
|
|
8
19
|
.neo-datepicker-popup {
|