@xh/hoist 56.2.0 → 56.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +90 -33
- package/admin/tabs/server/logViewer/LogDisplay.ts +4 -3
- package/admin/tabs/server/memory/MemoryMonitorModel.ts +3 -2
- package/admin/tabs/server/websocket/WebSocketModel.ts +1 -1
- package/appcontainer/AppContainerModel.ts +3 -1
- package/appcontainer/BannerModel.ts +24 -14
- package/appcontainer/BannerSourceModel.ts +19 -24
- package/cmp/ag-grid/AgGrid.ts +1 -0
- package/cmp/grid/Grid.ts +2 -2
- package/cmp/grid/columns/Column.ts +1 -1
- package/core/HoistComponent.ts +8 -9
- package/core/HoistProps.ts +4 -1
- package/core/XH.ts +5 -5
- package/core/model/Hooks.ts +2 -2
- package/core/types/Interfaces.ts +11 -2
- package/data/filter/FieldFilter.ts +40 -54
- package/desktop/appcontainer/Banner.scss +0 -2
- package/desktop/appcontainer/Banner.ts +2 -3
- package/desktop/cmp/dash/canvas/DashCanvasModel.ts +1 -1
- package/desktop/cmp/grid/impl/filter/ColumnHeaderFilter.scss +2 -2
- package/docs/upgrade-to-typescript.md +6 -1
- package/mobile/appcontainer/Banner.scss +1 -1
- package/mobile/appcontainer/Banner.ts +2 -3
- package/mobile/appcontainer/ExceptionDialogDetails.ts +3 -3
- package/package.json +8 -8
- package/styles/vars.scss +4 -7
- package/svc/AlertBannerService.ts +2 -0
- package/svc/FetchService.ts +15 -11
- package/utils/impl/TimeZone.ts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,18 +1,79 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## v56.4.0 - 2023-05-10
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
### 🎁 New Features
|
|
6
|
+
|
|
7
|
+
* Ensure that non-committed values are also checked when filtering a store with a FieldFilter.
|
|
8
|
+
This will maximize chances that records under edit will not disappear from user view due to
|
|
9
|
+
active filters.
|
|
10
|
+
|
|
11
|
+
### 🐞 Bug Fixes
|
|
12
|
+
|
|
13
|
+
* Fix bug where Grid ColumnHeaders could throw when `groupDisplayType` was set to `singleColumn`.
|
|
14
|
+
|
|
15
|
+
### ⚙️ Technical
|
|
16
|
+
* Adjustment to core model lookup in Hoist components to better support automated testing.
|
|
17
|
+
Components no longer strictly require rendering within an `AppContainer`.
|
|
18
|
+
|
|
19
|
+
### ⚙️ Typescript API Adjustments
|
|
20
|
+
|
|
21
|
+
* Improved return types for `FetchService` methods and corrected `FetchOptions` interface.
|
|
22
|
+
|
|
23
|
+
## v56.3.0 - 2023-05-08
|
|
24
|
+
|
|
25
|
+
### 🎁 New Features
|
|
26
|
+
|
|
27
|
+
* Added support for new `sortOrder` argument to `XH.showBanner()`. A default sort order is applied
|
|
28
|
+
if unspecified, ensuring banners do not unexpectedly change order when refreshed.
|
|
29
|
+
|
|
30
|
+
### ⚙️ Typescript API Adjustments
|
|
31
|
+
|
|
32
|
+
* Improved the recommendation for the app `declare` statement within
|
|
33
|
+
our [TypeScript migration docs](https://github.com/xh/hoist-react/blob/develop/docs/upgrade-to-typescript.md#bootstrapts--service-declarations).
|
|
34
|
+
* See this [Toolbox commit](https://github.com/xh/toolbox/commit/8df642cf) for a small,
|
|
35
|
+
recommended app-level change to improve autocompletion and usage checks within IntelliJ.
|
|
36
|
+
* Added generic support to `XH.message()` and `XH.prompt()` signatures with return type
|
|
37
|
+
of `Promise<T | boolean>`.
|
|
38
|
+
* Moved declaration of optional `children` prop to base `HoistProps` interface - required for TSX
|
|
39
|
+
support.
|
|
40
|
+
|
|
41
|
+
### ✨ Styles
|
|
42
|
+
|
|
43
|
+
* Removed `--xh-banner-height` CSS var.
|
|
44
|
+
* Desktop banners are implemented via `Toolbar`, which correctly sets a min height.
|
|
45
|
+
* Mobile banners now specify `min-height: 40px` via the `.xh-banner` class.
|
|
46
|
+
* This change allows banners containing custom components to grow to fit their contents without
|
|
47
|
+
requiring app-level CSS overrides.
|
|
48
|
+
* Added new `--xh-grid-filter-popover-[height|width]-px` CSS variables to support easier custom
|
|
49
|
+
sizing for grid column header filter popovers.
|
|
7
50
|
|
|
8
51
|
### ⚙️ Technical
|
|
9
|
-
* Optimize scrolling performance for `Grid` and `DataView`
|
|
10
52
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
53
|
+
* Updated internal config defaults to support latest AG Grid v29.3.4+ with use of
|
|
54
|
+
AG `suppressBrowserResizeObserver` config. Applications are encouraged to update to the latest AG
|
|
55
|
+
Grid dependencies to take advantage of ongoing performance updates.
|
|
56
|
+
|
|
57
|
+
## v56.2.0 - 2023-04-28
|
|
58
|
+
|
|
59
|
+
### 🎁 New Features
|
|
60
|
+
|
|
61
|
+
* Added `DashContainerModel.margin` config to customize the width of the resize splitters
|
|
62
|
+
between widgets.
|
|
63
|
+
|
|
64
|
+
### ⚙️ Technical
|
|
65
|
+
|
|
66
|
+
* Improve scrolling performance for `Grid` and `DataView` via internal configuration updates.
|
|
67
|
+
|
|
68
|
+
## v56.1.0 - 2023-04-14
|
|
69
|
+
|
|
70
|
+
### 🎁 New Features
|
|
71
|
+
|
|
72
|
+
* Display improved memory management diagnostics within Admin console Memory Monitor.
|
|
73
|
+
* New metrics require optional-but-recommended update to `hoist-core >= v16.1.0`.
|
|
14
74
|
|
|
15
75
|
### 🐞 Bug Fixes
|
|
76
|
+
|
|
16
77
|
* Fixes bug with display/reporting of exceptions during app initialization sequence.
|
|
17
78
|
|
|
18
79
|
## v56.0.0 - 2023-03-29
|
|
@@ -35,10 +96,12 @@
|
|
|
35
96
|
* Add a dependency on `@ag-grid-community/styles` to import new dedicated styles package.
|
|
36
97
|
* Imports of AG Grid CSS files within your app's `Bootstrap.ts` file will also need to be
|
|
37
98
|
updated to import styles from their new location. The recommended imports are now:
|
|
99
|
+
|
|
38
100
|
```typescript
|
|
39
101
|
import '@ag-grid-community/styles/ag-grid.css';
|
|
40
102
|
import '@ag-grid-community/styles/ag-theme-balham.css';
|
|
41
103
|
```
|
|
104
|
+
|
|
42
105
|
* New `xhActivityTrackingConfig` soft-configuration entry places new limits on the size of
|
|
43
106
|
any `data` objects passed to `XH.track()` calls.
|
|
44
107
|
* Any track requests with data objects exceeding this length will be persisted, but without the
|
|
@@ -72,20 +135,14 @@ import '@ag-grid-community/styles/ag-theme-balham.css';
|
|
|
72
135
|
|
|
73
136
|
## v55.4.0 - 2023-03-23
|
|
74
137
|
|
|
75
|
-
### 🐞 Bug Fixes
|
|
76
|
-
* Addresses `AgGrid` v28 regression whereby changing column visibility via state throws an
|
|
77
|
-
exception and doesn't
|
|
78
|
-
render the grid when column groups are set via the `groupId` property.
|
|
79
|
-
|
|
80
138
|
### 💥 Breaking Changes
|
|
81
|
-
* Hoist now requires `AgGrid` v29.1.0 or higher - update your `AgGrid` dependency in your app's
|
|
82
|
-
`package.json` file. See the [ag-Grid Changelog](https://www.ag-grid.com/changelog) for details.
|
|
83
|
-
* `AgGrid` stylesheets are now imported from the new `@ag-grid-community/styles` module. Update
|
|
84
|
-
your app's `Bootstrap.ts` file to import `ag-grid.css` and `ag-theme-balham.css` from this
|
|
85
|
-
module, and include it as a dependency in your app's `package.json` file.
|
|
86
139
|
|
|
87
|
-
|
|
88
|
-
|
|
140
|
+
* Requires AG Grid v29.0.0 or higher - see release notes for v56.0.0 above.
|
|
141
|
+
|
|
142
|
+
### 🐞 Bug Fixes
|
|
143
|
+
|
|
144
|
+
* Addresses `AgGrid` v28 regression whereby changing column visibility via state breaks grid
|
|
145
|
+
rendering when column groups are set via the `groupId` property.
|
|
89
146
|
|
|
90
147
|
## v55.3.2 - 2023-03-22
|
|
91
148
|
|
|
@@ -1026,7 +1083,7 @@ to use TypeScript for its own app-level code.
|
|
|
1026
1083
|
with the select library component and touch devices.
|
|
1027
1084
|
* Ensure `Column.autosizeBufferPx` is respected if provided.
|
|
1028
1085
|
|
|
1029
|
-
### ✨
|
|
1086
|
+
### ✨ Styles
|
|
1030
1087
|
|
|
1031
1088
|
* New `--xh-menu-item` CSS vars added, with tweaks to default desktop menu styling.
|
|
1032
1089
|
* Highlight background color added to mobile menu items while pressed.
|
|
@@ -1150,7 +1207,7 @@ to use TypeScript for its own app-level code.
|
|
|
1150
1207
|
* Triggering inline editing of text or select editor cells by typing characters will no longer lose
|
|
1151
1208
|
the first character pressed.
|
|
1152
1209
|
|
|
1153
|
-
### ✨
|
|
1210
|
+
### ✨ Styles
|
|
1154
1211
|
|
|
1155
1212
|
* New `TreeStyle.COLORS` and `TreeStyle.COLORS_AND_BORDERS` tree grid styles have been added. Use
|
|
1156
1213
|
the `--xh-grid-tree-group-color-level-*` CSS vars to customize colors as needed.
|
|
@@ -1192,7 +1249,7 @@ to use TypeScript for its own app-level code.
|
|
|
1192
1249
|
`agOptions`.
|
|
1193
1250
|
* Fixes an issue on iOS where `NumberInput` would incorrectly bring up a text keyboard.
|
|
1194
1251
|
|
|
1195
|
-
### ✨
|
|
1252
|
+
### ✨ Styles
|
|
1196
1253
|
|
|
1197
1254
|
* Reduced default Grid header and group row heights to minimize their use of vertical space,
|
|
1198
1255
|
especially at larger sizing modes. As before, apps can override via the `AgGrid.HEADER_HEIGHTS`
|
|
@@ -1217,7 +1274,7 @@ to use TypeScript for its own app-level code.
|
|
|
1217
1274
|
* The in-app changelog will no longer prompt the user with the "What's New" button if category-based
|
|
1218
1275
|
filtering results in a version without any release notes.
|
|
1219
1276
|
|
|
1220
|
-
### ✨
|
|
1277
|
+
### ✨ Styles
|
|
1221
1278
|
|
|
1222
1279
|
* New CSS vars added to support easier customization of desktop Tab font/size/color. Tabs now
|
|
1223
1280
|
respect standard `--xh-font-size` by default.
|
|
@@ -1239,7 +1296,7 @@ to use TypeScript for its own app-level code.
|
|
|
1239
1296
|
* Mobile `Select` input now supports async `queryFn` prop for parity with desktop.
|
|
1240
1297
|
* `TreeMapModel` now supports new `maxLabels` config for improved performance.
|
|
1241
1298
|
|
|
1242
|
-
### ✨
|
|
1299
|
+
### ✨ Styles
|
|
1243
1300
|
|
|
1244
1301
|
* Hoist's default font is now [Inter](https://rsms.me/inter/), shipped and bundled via the
|
|
1245
1302
|
`inter-ui` npm package. Inter is a modern, open-source font that leverages optical sizing to
|
|
@@ -1300,7 +1357,7 @@ to use TypeScript for its own app-level code.
|
|
|
1300
1357
|
|
|
1301
1358
|
* Fixed an issue preventing `FormField` labels from rendering if `fieldDefaults` was undefined.
|
|
1302
1359
|
|
|
1303
|
-
### ✨
|
|
1360
|
+
### ✨ Styles
|
|
1304
1361
|
|
|
1305
1362
|
* New `Badge.compact` prop sets size to half that of parent element when true (default false). The
|
|
1306
1363
|
`position` prop has been removed in favor of customizing placement of the component.
|
|
@@ -1354,7 +1411,7 @@ to use TypeScript for its own app-level code.
|
|
|
1354
1411
|
and
|
|
1355
1412
|
`selectedIds`, respectively, in `StoreSelectionModel`
|
|
1356
1413
|
|
|
1357
|
-
### ✨
|
|
1414
|
+
### ✨ Styles
|
|
1358
1415
|
|
|
1359
1416
|
* Higher contrast on grid context menus for improved legibility.
|
|
1360
1417
|
|
|
@@ -1422,7 +1479,7 @@ to use TypeScript for its own app-level code.
|
|
|
1422
1479
|
custom handling in a raw `AgGrid` component, see the example here:
|
|
1423
1480
|
https://www.ag-grid.com/javascript-grid/row-selection/#example-selection-with-keyboard-arrow-keys
|
|
1424
1481
|
|
|
1425
|
-
### ✨
|
|
1482
|
+
### ✨ Styles
|
|
1426
1483
|
|
|
1427
1484
|
* The red and green color values applied in dark mode have been lightened for improved legibility.
|
|
1428
1485
|
* The default `colorSpec` config for number formatters has changed to use new dedicated CSS classes
|
|
@@ -1478,7 +1535,7 @@ to use TypeScript for its own app-level code.
|
|
|
1478
1535
|
* `withShortDebug` has been deprecated. Use `withDebug` instead, which has the identical behavior.
|
|
1479
1536
|
This API simplification mirrors a recent change to `hoist-core`.
|
|
1480
1537
|
|
|
1481
|
-
### ✨
|
|
1538
|
+
### ✨ Styles
|
|
1482
1539
|
|
|
1483
1540
|
* If the first child of a `Placeholder` component is a Hoist icon, it will not automatically be
|
|
1484
1541
|
styled to 4x size with reduced opacity. (See new Toolbox example under the "Other" tab.)
|
|
@@ -1647,7 +1704,7 @@ your dev-utils dependency for your project to build.
|
|
|
1647
1704
|
* Improvements to exception serialization, especially for any raw javascript `Error` thrown by
|
|
1648
1705
|
client-side code.
|
|
1649
1706
|
|
|
1650
|
-
### ✨
|
|
1707
|
+
### ✨ Styles
|
|
1651
1708
|
|
|
1652
1709
|
* Buttons nested inline within desktop input components (e.g. clear buttons) tweaked to avoid
|
|
1653
1710
|
odd-looking background highlight on hover.
|
|
@@ -2000,7 +2057,7 @@ decorators, in favor of a simpler inheritance-based approach to defining models
|
|
|
2000
2057
|
* Fix issue where grid row striping inadvertently disabled by default for non-tree grids.
|
|
2001
2058
|
* Fix issue where grid empty text cleared on autosize.
|
|
2002
2059
|
|
|
2003
|
-
### ✨
|
|
2060
|
+
### ✨ Styles
|
|
2004
2061
|
|
|
2005
2062
|
* Default `Chart` themes reworked in both light and dark modes to better match overall Hoist theme.
|
|
2006
2063
|
|
|
@@ -2309,7 +2366,7 @@ below regarding related updates to `GridModel.columns` config processing.
|
|
|
2309
2366
|
* `StoreFilterField.filterOptions` has been removed. Set `filterIncludesChildren` directly on
|
|
2310
2367
|
the store instead.
|
|
2311
2368
|
|
|
2312
|
-
### ✨
|
|
2369
|
+
### ✨ Styles
|
|
2313
2370
|
|
|
2314
2371
|
* CSS variables for "intents" - most commonly used on buttons - have been reworked to use HSL color
|
|
2315
2372
|
values and support several standard variations of lightness and transparency.
|
|
@@ -3658,7 +3715,7 @@ leverage the context for model support discussed above.
|
|
|
3658
3715
|
* When checking for a possible expired session within `XH.handleException()`, prompt for app login
|
|
3659
3716
|
only for Ajax requests made to relative URLs (not e.g. remote APIs accessed via CORS). #1189
|
|
3660
3717
|
|
|
3661
|
-
### ✨
|
|
3718
|
+
### ✨ Styles
|
|
3662
3719
|
|
|
3663
3720
|
* Panel splitter collapse button more visible in dark theme. CSS vars to customize further fixed.
|
|
3664
3721
|
* The mobile app menu button has been moved to the right side of the top appBar, consistent with its
|
|
@@ -3915,7 +3972,7 @@ leverage the context for model support discussed above.
|
|
|
3915
3972
|
* FetchService's fetch methods no longer support `acceptJson` parameter. Instead, pass an {"Accept":
|
|
3916
3973
|
"application/json"} header using the `headers` parameter.
|
|
3917
3974
|
|
|
3918
|
-
### ✨
|
|
3975
|
+
### ✨ Styles
|
|
3919
3976
|
|
|
3920
3977
|
* Black point + grid colors adjusted in dark theme to better blend with overall blue-gray tint.
|
|
3921
3978
|
* Mobile styles have been adjusted to increase the default font size and grid row height, in
|
|
@@ -14,6 +14,7 @@ import {numberInput, switchInput, textInput} from '@xh/hoist/desktop/cmp/input';
|
|
|
14
14
|
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
15
15
|
import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
|
|
16
16
|
import {Icon} from '@xh/hoist/icon';
|
|
17
|
+
import {fmtTimeZone} from '@xh/hoist/utils/impl';
|
|
17
18
|
import {LogDisplayModel} from './LogDisplayModel';
|
|
18
19
|
import './LogViewer.scss';
|
|
19
20
|
|
|
@@ -87,11 +88,11 @@ const bbar = hoistCmp.factory(() => {
|
|
|
87
88
|
|
|
88
89
|
return toolbar({
|
|
89
90
|
items: [
|
|
90
|
-
'Server time:',
|
|
91
|
+
'Server time: ',
|
|
91
92
|
clock({
|
|
92
93
|
timezone: zone,
|
|
93
|
-
format: 'HH:mm
|
|
94
|
-
|
|
94
|
+
format: 'HH:mm',
|
|
95
|
+
suffix: fmtTimeZone(zone, XH.getEnv('serverTimeZoneOffset'))
|
|
95
96
|
})
|
|
96
97
|
],
|
|
97
98
|
omit: !zone // zone env support requires hoist-core 7.1+
|
|
@@ -87,7 +87,8 @@ export class MemoryMonitorModel extends HoistModel {
|
|
|
87
87
|
floor: 0,
|
|
88
88
|
top: '30%',
|
|
89
89
|
height: '70%',
|
|
90
|
-
title: {text: 'Heap (mb)'}
|
|
90
|
+
title: {text: 'Heap (mb)'},
|
|
91
|
+
offset: 0
|
|
91
92
|
}
|
|
92
93
|
],
|
|
93
94
|
tooltip: {outside: true, shared: true}
|
|
@@ -185,7 +186,7 @@ export class MemoryMonitorModel extends HoistModel {
|
|
|
185
186
|
async dumpHeapAsync() {
|
|
186
187
|
try {
|
|
187
188
|
const appEnv = XH.getEnv('appEnvironment').toLowerCase(),
|
|
188
|
-
filename = await XH.prompt({
|
|
189
|
+
filename = await XH.prompt<string>({
|
|
189
190
|
title: 'Dump Heap',
|
|
190
191
|
icon: Icon.fileArchive(),
|
|
191
192
|
message: `Specify a filename for the heap dump (to be saved in ${this.heapDumpDir})`,
|
|
@@ -93,7 +93,7 @@ export class WebSocketModel extends HoistModel {
|
|
|
93
93
|
const {selectedRecords} = this.gridModel;
|
|
94
94
|
if (isEmpty(selectedRecords)) return;
|
|
95
95
|
|
|
96
|
-
const message = await XH.prompt({
|
|
96
|
+
const message = await XH.prompt<string>({
|
|
97
97
|
title: 'Force suspend',
|
|
98
98
|
icon: Icon.stopCircle(),
|
|
99
99
|
confirmProps: {text: 'Force Suspend', icon: Icon.stopCircle(), intent: 'danger'},
|
|
@@ -19,6 +19,7 @@ import {SizingModeModel} from './SizingModeModel';
|
|
|
19
19
|
import {ViewportSizeModel} from './ViewportSizeModel';
|
|
20
20
|
import {ThemeModel} from './ThemeModel';
|
|
21
21
|
import {ToastSourceModel} from './ToastSourceModel';
|
|
22
|
+
import {BannerModel} from './BannerModel';
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Root object for Framework GUI State.
|
|
@@ -87,10 +88,11 @@ export class AppContainerModel extends HoistModel {
|
|
|
87
88
|
buttonText = mobile ? version : `Update to ${version}`;
|
|
88
89
|
|
|
89
90
|
XH.showBanner({
|
|
90
|
-
category: '
|
|
91
|
+
category: 'xhAppUpdate',
|
|
91
92
|
message,
|
|
92
93
|
icon: Icon.rocket({size: 'lg'}),
|
|
93
94
|
intent: 'warning',
|
|
95
|
+
sortOrder: BannerModel.BANNER_SORTS.APP_UPDATE,
|
|
94
96
|
enableClose: false,
|
|
95
97
|
actionButtonProps: {
|
|
96
98
|
icon: Icon.refresh(),
|
|
@@ -18,36 +18,46 @@ export class BannerModel extends HoistModel {
|
|
|
18
18
|
icon;
|
|
19
19
|
message;
|
|
20
20
|
intent;
|
|
21
|
+
sortOrder;
|
|
21
22
|
className;
|
|
22
23
|
enableClose;
|
|
23
24
|
onClose;
|
|
24
25
|
onClick;
|
|
25
26
|
actionButtonProps;
|
|
26
|
-
props;
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
actionButtonProps,
|
|
38
|
-
...props
|
|
39
|
-
}: BannerSpec) {
|
|
28
|
+
/**
|
|
29
|
+
* Sort order for Hoist-provided banners.
|
|
30
|
+
*/
|
|
31
|
+
static BANNER_SORTS = {
|
|
32
|
+
APP_UPDATE: -2,
|
|
33
|
+
ADMIN_ALERT: -1
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
constructor(spec: BannerSpec) {
|
|
40
37
|
super();
|
|
41
38
|
|
|
39
|
+
const {
|
|
40
|
+
category = 'default',
|
|
41
|
+
icon,
|
|
42
|
+
message,
|
|
43
|
+
intent = 'primary',
|
|
44
|
+
sortOrder,
|
|
45
|
+
className,
|
|
46
|
+
enableClose = true,
|
|
47
|
+
onClose,
|
|
48
|
+
onClick,
|
|
49
|
+
actionButtonProps
|
|
50
|
+
} = spec;
|
|
51
|
+
|
|
42
52
|
this.category = category;
|
|
43
53
|
this.icon = icon;
|
|
44
54
|
this.message = message;
|
|
45
55
|
this.intent = intent;
|
|
56
|
+
this.sortOrder = sortOrder;
|
|
46
57
|
this.className = className;
|
|
47
58
|
this.enableClose = enableClose;
|
|
48
59
|
this.onClose = onClose;
|
|
49
60
|
this.onClick = onClick;
|
|
50
61
|
this.actionButtonProps = actionButtonProps;
|
|
51
|
-
this.props = props;
|
|
52
62
|
}
|
|
53
63
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {XH, HoistModel, managed, BannerSpec} from '@xh/hoist/core';
|
|
8
8
|
import {action, observable, makeObservable} from '@xh/hoist/mobx';
|
|
9
|
-
import {find, reject} from 'lodash';
|
|
9
|
+
import {find, reject, sortBy, without, last} from 'lodash';
|
|
10
10
|
|
|
11
11
|
import {BannerModel} from './BannerModel';
|
|
12
12
|
|
|
@@ -22,17 +22,29 @@ export class BannerSourceModel extends HoistModel {
|
|
|
22
22
|
@observable.ref
|
|
23
23
|
bannerModels: BannerModel[] = [];
|
|
24
24
|
|
|
25
|
-
MAX_BANNERS = 4;
|
|
26
|
-
|
|
27
25
|
constructor() {
|
|
28
26
|
super();
|
|
29
27
|
makeObservable(this);
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
@action
|
|
33
|
-
show(
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
show(spec: BannerSpec): BannerModel {
|
|
32
|
+
let {bannerModels} = this,
|
|
33
|
+
ret = new BannerModel(spec);
|
|
34
|
+
|
|
35
|
+
// Removes banner from new banner's category if it exists.
|
|
36
|
+
const existing = find(bannerModels, {category: ret.category});
|
|
37
|
+
if (existing) {
|
|
38
|
+
bannerModels = without(bannerModels, existing);
|
|
39
|
+
XH.safeDestroy(existing);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Place in requested pos, existing pos, or last
|
|
43
|
+
const maxSortOrder = last(bannerModels)?.sortOrder ?? 0;
|
|
44
|
+
ret.sortOrder = spec.sortOrder ?? existing?.sortOrder ?? maxSortOrder + 1;
|
|
45
|
+
bannerModels = sortBy([...bannerModels, ret], 'sortOrder');
|
|
46
|
+
|
|
47
|
+
this.bannerModels = bannerModels;
|
|
36
48
|
return ret;
|
|
37
49
|
}
|
|
38
50
|
|
|
@@ -43,24 +55,7 @@ export class BannerSourceModel extends HoistModel {
|
|
|
43
55
|
this.bannerModels = reject(this.bannerModels, {category});
|
|
44
56
|
}
|
|
45
57
|
|
|
46
|
-
|
|
47
|
-
// Implementation
|
|
48
|
-
//------------------------------------
|
|
49
|
-
@action
|
|
50
|
-
addModel(model: BannerModel) {
|
|
51
|
-
// Remove existing banner for category
|
|
52
|
-
this.hide(model.category);
|
|
53
|
-
|
|
54
|
-
// Add new banner, removing old banners if limit exceeded
|
|
55
|
-
const models = [...this.bannerModels, model];
|
|
56
|
-
while (models.length > this.MAX_BANNERS) {
|
|
57
|
-
const bannerModel = models.shift();
|
|
58
|
-
XH.safeDestroy(bannerModel);
|
|
59
|
-
}
|
|
60
|
-
this.bannerModels = models;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
getBanner(category: string): BannerModel {
|
|
58
|
+
private getBanner(category: string): BannerModel {
|
|
64
59
|
return find(this.bannerModels, {category});
|
|
65
60
|
}
|
|
66
61
|
}
|
package/cmp/ag-grid/AgGrid.ts
CHANGED
|
@@ -84,6 +84,7 @@ export const [AgGrid, agGrid] = hoistCmp.withFactory<AgGridProps>({
|
|
|
84
84
|
item: createElement(AgGridReact, {
|
|
85
85
|
// Default some ag-grid props, but allow overriding.
|
|
86
86
|
getRowHeight: impl.getRowHeight,
|
|
87
|
+
suppressBrowserResizeObserver: true,
|
|
87
88
|
// Pass others on directly.
|
|
88
89
|
...agGridProps,
|
|
89
90
|
|
package/cmp/grid/Grid.ts
CHANGED
|
@@ -197,8 +197,8 @@ class GridLocalModel extends HoistModel {
|
|
|
197
197
|
clipboardCopy: Icon.copy({asHtml: true})
|
|
198
198
|
},
|
|
199
199
|
components: {
|
|
200
|
-
agColumnHeader: props => columnHeader(props),
|
|
201
|
-
agColumnGroupHeader: props => columnGroupHeader(props)
|
|
200
|
+
agColumnHeader: props => columnHeader({...props, gridModel: model}),
|
|
201
|
+
agColumnGroupHeader: props => columnGroupHeader({...props, gridModel: model})
|
|
202
202
|
},
|
|
203
203
|
rowSelection: selModel.mode == 'disabled' ? undefined : selModel.mode,
|
|
204
204
|
suppressRowClickSelection: !selModel.isEnabled,
|
|
@@ -703,7 +703,7 @@ export class Column {
|
|
|
703
703
|
lockPinned: !gridModel.enableColumnPinning || XH.isMobileApp,
|
|
704
704
|
pinned: this.pinned,
|
|
705
705
|
lockVisible: !this.hideable || !gridModel.colChooserModel || XH.isMobileApp,
|
|
706
|
-
headerComponentParams: {
|
|
706
|
+
headerComponentParams: {xhColumn: this},
|
|
707
707
|
suppressColumnsToolPanel: this.excludeFromChooser,
|
|
708
708
|
suppressFiltersToolPanel: this.excludeFromChooser,
|
|
709
709
|
enableCellChangeFlash: this.highlightOnChange,
|
package/core/HoistComponent.ts
CHANGED
|
@@ -43,8 +43,10 @@ import {
|
|
|
43
43
|
} from 'react';
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
46
|
+
* Type representing props passed to a HoistComponent's render function.
|
|
47
|
+
*
|
|
48
|
+
* This type removes from its base type several props that are used by HoistComponent itself and
|
|
49
|
+
* not provided to the render function.
|
|
48
50
|
*/
|
|
49
51
|
export type RenderPropsOf<P extends HoistProps> = P & {
|
|
50
52
|
/** Pre-processed by HoistComponent internals into a mounted model. Never passed to render. */
|
|
@@ -52,15 +54,12 @@ export type RenderPropsOf<P extends HoistProps> = P & {
|
|
|
52
54
|
|
|
53
55
|
/** Pre-processed by HoistComponent internals and attached to model. Never passed to render. */
|
|
54
56
|
modelRef: never;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* React Children. Populated on props by React internally, before rendering. Applications
|
|
58
|
-
* will typically provide children to a component via JSX or the `item(s)` property passed to
|
|
59
|
-
* an element factory.
|
|
60
|
-
*/
|
|
61
|
-
children?: ReactNode;
|
|
62
57
|
};
|
|
63
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Configuration for creating a Component. May be specified either as a render function,
|
|
61
|
+
* or an object containing a render function and associated metadata.
|
|
62
|
+
*/
|
|
64
63
|
export type ComponentConfig<P extends HoistProps> =
|
|
65
64
|
| ((props: RenderPropsOf<P>, ref?: ForwardedRef<any>) => ReactNode)
|
|
66
65
|
| {
|
package/core/HoistProps.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Copyright © 2022 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {HoistModel} from '@xh/hoist/core/model';
|
|
8
|
-
import {CSSProperties, HTMLAttributes, Ref} from 'react';
|
|
8
|
+
import {CSSProperties, HTMLAttributes, ReactNode, Ref} from 'react';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Props interface for Hoist Components.
|
|
@@ -39,6 +39,9 @@ export interface HoistProps<M extends HoistModel = HoistModel> {
|
|
|
39
39
|
* any base class name provided by the component definition itself.
|
|
40
40
|
*/
|
|
41
41
|
className?: string;
|
|
42
|
+
|
|
43
|
+
/** React children. */
|
|
44
|
+
children?: ReactNode;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
/**
|
package/core/XH.ts
CHANGED
|
@@ -453,7 +453,7 @@ export class XHApi {
|
|
|
453
453
|
* @returns true if user confirms, false if user cancels. If an input is provided, the
|
|
454
454
|
* Promise will resolve to the input value if user confirms.
|
|
455
455
|
*/
|
|
456
|
-
message(config: MessageSpec): Promise<
|
|
456
|
+
message<T = unknown>(config: MessageSpec): Promise<T | boolean> {
|
|
457
457
|
return this.acm.messageSourceModel.message(config);
|
|
458
458
|
}
|
|
459
459
|
|
|
@@ -484,7 +484,7 @@ export class XHApi {
|
|
|
484
484
|
*
|
|
485
485
|
* @returns value of input if user confirms, false if user cancels.
|
|
486
486
|
*/
|
|
487
|
-
prompt(config: MessageSpec): Promise<
|
|
487
|
+
prompt<T = unknown>(config: MessageSpec): Promise<T | false> {
|
|
488
488
|
return this.acm.messageSourceModel.prompt(config);
|
|
489
489
|
}
|
|
490
490
|
|
|
@@ -524,9 +524,9 @@ export class XHApi {
|
|
|
524
524
|
* Show a Banner across the top of the viewport. Banners are unique by their
|
|
525
525
|
* category prop - showing a new banner with an existing category will replace it.
|
|
526
526
|
*/
|
|
527
|
-
showBanner(
|
|
528
|
-
if (isString(
|
|
529
|
-
return this.acm.bannerSourceModel.show(
|
|
527
|
+
showBanner(spec: BannerSpec | string): BannerModel {
|
|
528
|
+
if (isString(spec)) spec = {message: spec};
|
|
529
|
+
return this.acm.bannerSourceModel.show(spec);
|
|
530
530
|
}
|
|
531
531
|
|
|
532
532
|
/**
|
package/core/model/Hooks.ts
CHANGED
|
@@ -67,7 +67,7 @@ export function useModelLinker(model: HoistModel, modelLookup: ModelLookup, prop
|
|
|
67
67
|
if (isLinking) {
|
|
68
68
|
model._modelLookup = modelLookup;
|
|
69
69
|
each(model['_xhInjectedParentProperties'], (selector, name) => {
|
|
70
|
-
const parentModel = modelLookup
|
|
70
|
+
const parentModel = modelLookup?.lookupModel(selector);
|
|
71
71
|
if (!parentModel) {
|
|
72
72
|
throw XH.exception(
|
|
73
73
|
`Failed to resolve @lookup for property '${name}' with selector ${formatSelector(
|
|
@@ -81,7 +81,7 @@ export function useModelLinker(model: HoistModel, modelLookup: ModelLookup, prop
|
|
|
81
81
|
|
|
82
82
|
// Linked models with an impl parent that are not explicitly marked should be marked as impl.
|
|
83
83
|
if (isUndefined(model.xhImpl)) {
|
|
84
|
-
const parentModel = modelLookup
|
|
84
|
+
const parentModel = modelLookup?.lookupModel('*');
|
|
85
85
|
if (parentModel?.xhImpl === true) {
|
|
86
86
|
model.xhImpl = true;
|
|
87
87
|
}
|
package/core/types/Interfaces.ts
CHANGED
|
@@ -134,7 +134,6 @@ export interface MessageSpec {
|
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
136
|
* Configuration object for an app-wide banner.
|
|
137
|
-
* Additional properties passed to this object will be passed directly to the banner component.
|
|
138
137
|
*/
|
|
139
138
|
export interface BannerSpec {
|
|
140
139
|
message?: ReactNode;
|
|
@@ -142,7 +141,17 @@ export interface BannerSpec {
|
|
|
142
141
|
intent?: Intent;
|
|
143
142
|
className?: string;
|
|
144
143
|
|
|
145
|
-
/**
|
|
144
|
+
/**
|
|
145
|
+
* Determines order in which banner will be displayed.
|
|
146
|
+
* If not provided, banner will be placed below any existing banners.
|
|
147
|
+
* @see BannerModel.BANNER_SORTS
|
|
148
|
+
*/
|
|
149
|
+
sortOrder?: number;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Showing a banner with a given category will hide any
|
|
153
|
+
* preexisting banner with the same category.
|
|
154
|
+
*/
|
|
146
155
|
category?: string;
|
|
147
156
|
|
|
148
157
|
/**
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from 'lodash';
|
|
20
20
|
import {parseFieldValue} from '../Field';
|
|
21
21
|
import {Store} from '../Store';
|
|
22
|
+
import {StoreRecord} from '../StoreRecord';
|
|
22
23
|
import {Filter} from './Filter';
|
|
23
24
|
import {FieldFilterOperator, FieldFilterSpec, FilterTestFn} from './Types';
|
|
24
25
|
|
|
@@ -111,91 +112,76 @@ export class FieldFilter extends Filter {
|
|
|
111
112
|
? value.map(v => parseFieldValue(v, fieldType))
|
|
112
113
|
: parseFieldValue(value, fieldType);
|
|
113
114
|
}
|
|
114
|
-
const getVal = store ? r => r.committedData[field] : r => r[field],
|
|
115
|
-
doNotFilter = r => store && isNil(r.committedData); // Ignore (do not filter out) record if part of a store and it has no committed data
|
|
116
115
|
|
|
117
116
|
if (FieldFilter.ARRAY_OPERATORS.includes(op)) {
|
|
118
117
|
value = castArray(value);
|
|
119
118
|
}
|
|
120
119
|
|
|
120
|
+
let opFn: (v: any) => boolean;
|
|
121
121
|
switch (op) {
|
|
122
122
|
case '=':
|
|
123
|
-
|
|
124
|
-
if (doNotFilter(r)) return true;
|
|
125
|
-
let v = getVal(r);
|
|
123
|
+
opFn = v => {
|
|
126
124
|
if (isNil(v) || v === '') v = null;
|
|
127
125
|
return value.includes(v);
|
|
128
126
|
};
|
|
127
|
+
break;
|
|
129
128
|
case '!=':
|
|
130
|
-
|
|
131
|
-
if (doNotFilter(r)) return true;
|
|
132
|
-
let v = getVal(r);
|
|
129
|
+
opFn = v => {
|
|
133
130
|
if (isNil(v) || v === '') v = null;
|
|
134
131
|
return !value.includes(v);
|
|
135
132
|
};
|
|
133
|
+
break;
|
|
136
134
|
case '>':
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const v = getVal(r);
|
|
140
|
-
return !isNil(v) && v > value;
|
|
141
|
-
};
|
|
135
|
+
opFn = v => !isNil(v) && v > value;
|
|
136
|
+
break;
|
|
142
137
|
case '>=':
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const v = getVal(r);
|
|
146
|
-
return !isNil(v) && v >= value;
|
|
147
|
-
};
|
|
138
|
+
opFn = v => !isNil(v) && v >= value;
|
|
139
|
+
break;
|
|
148
140
|
case '<':
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const v = getVal(r);
|
|
152
|
-
return !isNil(v) && v < value;
|
|
153
|
-
};
|
|
141
|
+
opFn = v => !isNil(v) && v < value;
|
|
142
|
+
break;
|
|
154
143
|
case '<=':
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const v = getVal(r);
|
|
158
|
-
return !isNil(v) && v <= value;
|
|
159
|
-
};
|
|
144
|
+
opFn = v => !isNil(v) && v <= value;
|
|
145
|
+
break;
|
|
160
146
|
case 'like':
|
|
161
147
|
regExps = value.map(v => new RegExp(escapeRegExp(v), 'i'));
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return regExps.some(re => re.test(getVal(r)));
|
|
165
|
-
};
|
|
148
|
+
opFn = v => regExps.some(re => re.test(v));
|
|
149
|
+
break;
|
|
166
150
|
case 'not like':
|
|
167
151
|
regExps = value.map(v => new RegExp(escapeRegExp(v), 'i'));
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return regExps.every(re => !re.test(getVal(r)));
|
|
171
|
-
};
|
|
152
|
+
opFn = v => regExps.every(re => !re.test(v));
|
|
153
|
+
break;
|
|
172
154
|
case 'begins':
|
|
173
155
|
regExps = value.map(v => new RegExp('^' + escapeRegExp(v), 'i'));
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return regExps.some(re => re.test(getVal(r)));
|
|
177
|
-
};
|
|
156
|
+
opFn = v => regExps.some(re => re.test(v));
|
|
157
|
+
break;
|
|
178
158
|
case 'ends':
|
|
179
159
|
regExps = value.map(v => new RegExp(escapeRegExp(v) + '$', 'i'));
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return regExps.some(re => re.test(getVal(r)));
|
|
183
|
-
};
|
|
160
|
+
opFn = v => regExps.some(re => re.test(v));
|
|
161
|
+
break;
|
|
184
162
|
case 'includes':
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const v = getVal(r);
|
|
188
|
-
return !isNil(v) && v.some(it => value.includes(it));
|
|
189
|
-
};
|
|
163
|
+
opFn = v => !isNil(v) && v.some(it => value.includes(it));
|
|
164
|
+
break;
|
|
190
165
|
case 'excludes':
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const v = getVal(r);
|
|
194
|
-
return isNil(v) || !v.some(it => value.includes(it));
|
|
195
|
-
};
|
|
166
|
+
opFn = v => isNil(v) || !v.some(it => value.includes(it));
|
|
167
|
+
break;
|
|
196
168
|
default:
|
|
197
169
|
throw XH.exception(`Unknown operator: ${op}`);
|
|
198
170
|
}
|
|
171
|
+
|
|
172
|
+
if (!store) return r => opFn(r[field]);
|
|
173
|
+
|
|
174
|
+
return (r: StoreRecord) => {
|
|
175
|
+
const val = r.get(field);
|
|
176
|
+
if (opFn(val)) return true;
|
|
177
|
+
|
|
178
|
+
// Maximize chances of matching. Always pass adds ...
|
|
179
|
+
if (r.isAdd) return true;
|
|
180
|
+
|
|
181
|
+
// ... and check any differing original value as well
|
|
182
|
+
const committedVal = r.committedData[field];
|
|
183
|
+
return committedVal !== val && opFn(committedVal);
|
|
184
|
+
};
|
|
199
185
|
}
|
|
200
186
|
|
|
201
187
|
override equals(other: Filter): boolean {
|
|
@@ -25,7 +25,7 @@ export const banner = hoistCmp.factory({
|
|
|
25
25
|
model: uses(BannerModel),
|
|
26
26
|
|
|
27
27
|
render({model}) {
|
|
28
|
-
const {icon, message, intent, onClick, className
|
|
28
|
+
const {icon, message, intent, onClick, className} = model;
|
|
29
29
|
|
|
30
30
|
return toolbar({
|
|
31
31
|
className: classNames(
|
|
@@ -50,8 +50,7 @@ export const banner = hoistCmp.factory({
|
|
|
50
50
|
}),
|
|
51
51
|
actionButton(),
|
|
52
52
|
dismissButton()
|
|
53
|
-
]
|
|
54
|
-
...props
|
|
53
|
+
]
|
|
55
54
|
});
|
|
56
55
|
}
|
|
57
56
|
});
|
|
@@ -98,7 +98,9 @@ rename with a `.ts` extension to ensure you have at least one TS file in your bu
|
|
|
98
98
|
In that same file, add a declaration statement to let TS know about any of your application
|
|
99
99
|
services (`HoistService` instances) that you are initializing. Those are installed on and referenced
|
|
100
100
|
from the `XH` object; for TS to consider references to those services valid, it needs to know that
|
|
101
|
-
the type of the `XH` singleton (`XHApi`) has a property for each of your services.
|
|
101
|
+
the type of the `XH` singleton (`XHApi`) has a property for each of your services. For users of
|
|
102
|
+
IntelliJ, an ignored re-declaration of the XH singleton with this interface helps the IDE properly
|
|
103
|
+
notice uses of these services.
|
|
102
104
|
|
|
103
105
|
In Toolbox, we have the following within `Bootstrap.ts` to declare five TB-specific services (your
|
|
104
106
|
services will vary of course) and a custom property installed on `HoistUser`, the type returned
|
|
@@ -106,6 +108,7 @@ by `XH.getUser()`:
|
|
|
106
108
|
|
|
107
109
|
```typescript
|
|
108
110
|
declare module '@xh/hoist/core' {
|
|
111
|
+
// Merge interface with XHApi class to include injected services.
|
|
109
112
|
export interface XHApi {
|
|
110
113
|
contactService: ContactService;
|
|
111
114
|
gitHubService: GitHubService;
|
|
@@ -113,6 +116,8 @@ declare module '@xh/hoist/core' {
|
|
|
113
116
|
portfolioService: PortfolioService;
|
|
114
117
|
taskService: TaskService;
|
|
115
118
|
}
|
|
119
|
+
// @ts-ignore - Help IntelliJ recognize uses of injected service methods from the `XH` singleton.
|
|
120
|
+
export const XH: XHApi;
|
|
116
121
|
|
|
117
122
|
export interface HoistUser {
|
|
118
123
|
profilePicUrl: string;
|
|
@@ -8,7 +8,7 @@ body.xh-app .xh-banner {
|
|
|
8
8
|
padding: var(--xh-pad-half-px) var(--xh-pad-half-px) var(--xh-pad-half-px) var(--xh-pad-px);
|
|
9
9
|
display: flex;
|
|
10
10
|
align-items: center;
|
|
11
|
-
height:
|
|
11
|
+
min-height: 40px;
|
|
12
12
|
|
|
13
13
|
&__click_target > .xh-icon {
|
|
14
14
|
margin-right: var(--xh-pad-px);
|
|
@@ -23,7 +23,7 @@ export const banner = hoistCmp.factory({
|
|
|
23
23
|
displayName: 'Banner',
|
|
24
24
|
model: uses(BannerModel),
|
|
25
25
|
render({model}) {
|
|
26
|
-
const {icon, message, intent, onClick, className
|
|
26
|
+
const {icon, message, intent, onClick, className} = model;
|
|
27
27
|
|
|
28
28
|
return div({
|
|
29
29
|
className: classNames(
|
|
@@ -48,8 +48,7 @@ export const banner = hoistCmp.factory({
|
|
|
48
48
|
}),
|
|
49
49
|
actionButton(),
|
|
50
50
|
dismissButton()
|
|
51
|
-
]
|
|
52
|
-
...props
|
|
51
|
+
]
|
|
53
52
|
});
|
|
54
53
|
}
|
|
55
54
|
});
|
|
@@ -51,7 +51,7 @@ export const exceptionDialogDetails = hoistCmp.factory({
|
|
|
51
51
|
icon: Icon.envelope(),
|
|
52
52
|
text: 'Send Message',
|
|
53
53
|
onClick: () => {
|
|
54
|
-
XH.prompt({
|
|
54
|
+
XH.prompt<string>({
|
|
55
55
|
title: 'Send Message',
|
|
56
56
|
message: null,
|
|
57
57
|
input: {
|
|
@@ -63,8 +63,8 @@ export const exceptionDialogDetails = hoistCmp.factory({
|
|
|
63
63
|
},
|
|
64
64
|
confirmProps: {icon: Icon.envelope(), text: 'Send'}
|
|
65
65
|
}).then(userMessage => {
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
if (userMessage) {
|
|
67
|
+
model.userMessage = userMessage;
|
|
68
68
|
model.sendReportAsync();
|
|
69
69
|
}
|
|
70
70
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "56.
|
|
3
|
+
"version": "56.4.0",
|
|
4
4
|
"description": "Hoist add-on for building and deploying React Applications.",
|
|
5
5
|
"repository": "github:xh/hoist-react",
|
|
6
6
|
"homepage": "https://xh.io",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"classnames": "~2.3.2",
|
|
46
46
|
"clipboard-copy": "~4.0.1",
|
|
47
47
|
"codemirror": "~5.65.0",
|
|
48
|
-
"core-js": "^3.
|
|
48
|
+
"core-js": "^3.0",
|
|
49
49
|
"debounce-promise": "~3.1.0",
|
|
50
50
|
"dompurify": "~3.0.1",
|
|
51
51
|
"downloadjs": "~1.4.7",
|
|
@@ -90,15 +90,15 @@
|
|
|
90
90
|
"@xh/hoist-dev-utils": "^6.1.2",
|
|
91
91
|
"eslint-config-prettier": "8.x",
|
|
92
92
|
"eslint-plugin-tsdoc": "^0.2.17",
|
|
93
|
-
"husky": "^8.0.
|
|
94
|
-
"lint-staged": "^13.
|
|
95
|
-
"postcss": "^8.4.
|
|
96
|
-
"prettier": "^2.8.
|
|
93
|
+
"husky": "^8.0.3",
|
|
94
|
+
"lint-staged": "^13.2.2",
|
|
95
|
+
"postcss": "^8.4.23",
|
|
96
|
+
"prettier": "^2.8.8",
|
|
97
97
|
"react": "^18.2.0",
|
|
98
98
|
"react-dom": "^18.2.0",
|
|
99
|
-
"stylelint": "^15.
|
|
99
|
+
"stylelint": "^15.6.1",
|
|
100
100
|
"stylelint-config-standard-scss": "^7.0.1",
|
|
101
|
-
"type-fest": "^3.
|
|
101
|
+
"type-fest": "^3.10.0",
|
|
102
102
|
"typescript": "~4.9.5"
|
|
103
103
|
},
|
|
104
104
|
"resolutions": {
|
package/styles/vars.scss
CHANGED
|
@@ -502,6 +502,10 @@ body {
|
|
|
502
502
|
--xh-grid-multifield-line-height: var(--grid-multifield-line-height, 14);
|
|
503
503
|
--xh-grid-multifield-line-height-px: calc(var(--xh-grid-multifield-line-height) * 1px);
|
|
504
504
|
|
|
505
|
+
// Grid column-header-based filter popover (desktop only)
|
|
506
|
+
--xh-grid-filter-popover-height-px: var(--grid-filter-popover-height-px, 350px);
|
|
507
|
+
--xh-grid-filter-popover-width-px: var(--grid-filter-popover-width-px, 240px);
|
|
508
|
+
|
|
505
509
|
// Dark Grid
|
|
506
510
|
&.xh-dark {
|
|
507
511
|
--xh-grid-bg-hover: var(--grid-bg-hover, hsl(200, 50%, 22%));
|
|
@@ -664,13 +668,6 @@ body {
|
|
|
664
668
|
}
|
|
665
669
|
|
|
666
670
|
|
|
667
|
-
//------------------------
|
|
668
|
-
// Banners
|
|
669
|
-
//------------------------
|
|
670
|
-
--xh-banner-height: var(--banner-height, 40);
|
|
671
|
-
--xh-banner-height-px: calc(var(--xh-banner-height) * 1px);
|
|
672
|
-
|
|
673
|
-
|
|
674
671
|
//------------------------
|
|
675
672
|
// Tabs
|
|
676
673
|
//------------------------
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2022 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
+
import {BannerModel} from '@xh/hoist/appcontainer/BannerModel';
|
|
7
8
|
import {div, p} from '@xh/hoist/cmp/layout';
|
|
8
9
|
import {BannerSpec, HoistService, Intent, managed, XH} from '@xh/hoist/core';
|
|
9
10
|
import {Icon} from '@xh/hoist/icon';
|
|
@@ -96,6 +97,7 @@ export class AlertBannerService extends HoistService {
|
|
|
96
97
|
intent,
|
|
97
98
|
icon,
|
|
98
99
|
enableClose,
|
|
100
|
+
sortOrder: BannerModel.BANNER_SORTS.ADMIN_ALERT,
|
|
99
101
|
actionButtonProps,
|
|
100
102
|
onClick
|
|
101
103
|
};
|
package/svc/FetchService.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2022 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {HoistService, XH, Exception, PlainObject, Thunkable} from '@xh/hoist/core';
|
|
7
|
+
import {HoistService, XH, Exception, PlainObject, Thunkable, FetchResponse} from '@xh/hoist/core';
|
|
8
8
|
import {isLocalDate, SECONDS, ONE_MINUTE, olderThan} from '@xh/hoist/utils/datetime';
|
|
9
9
|
import {throwIf} from '@xh/hoist/utils/js';
|
|
10
10
|
import {StatusCodes} from 'http-status-codes';
|
|
@@ -61,10 +61,8 @@ export class FetchService extends HoistService {
|
|
|
61
61
|
* Send a request via the underlying fetch API.
|
|
62
62
|
* @returns Promise which resolves to a Fetch Response.
|
|
63
63
|
*/
|
|
64
|
-
fetch(opts: FetchOptions): Promise<
|
|
65
|
-
return this.managedFetchAsync(opts, aborter =>
|
|
66
|
-
this.fetchInternalAsync(opts, aborter)
|
|
67
|
-
) as any;
|
|
64
|
+
fetch(opts: FetchOptions): Promise<FetchResponse> {
|
|
65
|
+
return this.managedFetchAsync(opts, aborter => this.fetchInternalAsync(opts, aborter));
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
/**
|
|
@@ -81,7 +79,7 @@ export class FetchService extends HoistService {
|
|
|
81
79
|
aborter
|
|
82
80
|
);
|
|
83
81
|
return this.NO_JSON_RESPONSES.includes(r.status) ? null : r.json();
|
|
84
|
-
})
|
|
82
|
+
});
|
|
85
83
|
}
|
|
86
84
|
|
|
87
85
|
/**
|
|
@@ -142,7 +140,10 @@ export class FetchService extends HoistService {
|
|
|
142
140
|
//-----------------------
|
|
143
141
|
// Implementation
|
|
144
142
|
//-----------------------
|
|
145
|
-
private async managedFetchAsync(
|
|
143
|
+
private async managedFetchAsync(
|
|
144
|
+
opts: FetchOptions,
|
|
145
|
+
fn: (ctl: AbortController) => Promise<FetchResponse>
|
|
146
|
+
): Promise<FetchResponse> {
|
|
146
147
|
const {autoAborters, defaultTimeout} = this,
|
|
147
148
|
{autoAbortKey, timeout = defaultTimeout} = opts,
|
|
148
149
|
aborter = new AbortController();
|
|
@@ -177,7 +178,7 @@ export class FetchService extends HoistService {
|
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
private async fetchInternalAsync(opts, aborter): Promise<
|
|
181
|
+
private async fetchInternalAsync(opts, aborter): Promise<FetchResponse> {
|
|
181
182
|
const {defaultHeaders} = this;
|
|
182
183
|
let {url, method, headers, body, params} = opts;
|
|
183
184
|
throwIf(!url, 'No url specified in call to fetchService.');
|
|
@@ -236,7 +237,7 @@ export class FetchService extends HoistService {
|
|
|
236
237
|
}
|
|
237
238
|
}
|
|
238
239
|
|
|
239
|
-
const ret
|
|
240
|
+
const ret = (await fetch(url, fetchOpts)) as FetchResponse;
|
|
240
241
|
|
|
241
242
|
if (!ret.ok) {
|
|
242
243
|
ret.responseText = await this.safeResponseTextAsync(ret);
|
|
@@ -300,8 +301,11 @@ export interface FetchOptions {
|
|
|
300
301
|
/** URL for the request. Relative urls will be appended to XH.baseUrl. */
|
|
301
302
|
url: string;
|
|
302
303
|
|
|
303
|
-
/**
|
|
304
|
-
|
|
304
|
+
/**
|
|
305
|
+
* Data to send in the request body (for POSTs/PUTs of JSON).
|
|
306
|
+
* When using `fetch`, provide a string. Otherwise, provide a PlainObject.
|
|
307
|
+
*/
|
|
308
|
+
body?: PlainObject | string;
|
|
305
309
|
|
|
306
310
|
/**
|
|
307
311
|
* Parameters to encode and append as a query string, or send with the request body
|
package/utils/impl/TimeZone.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {fmtNumber} from '@xh/hoist/format';
|
|
|
13
13
|
export function fmtTimeZone(name: string, offset: number): string {
|
|
14
14
|
if (!name) return '';
|
|
15
15
|
|
|
16
|
-
return name
|
|
17
|
-
?
|
|
18
|
-
: `${name}`;
|
|
16
|
+
return name === 'GMT' || name === 'UTC'
|
|
17
|
+
? name
|
|
18
|
+
: `${name} (GMT${fmtNumber(offset / HOURS, {withPlusSign: true, asHtml: true})})`;
|
|
19
19
|
}
|