@xh/hoist 74.0.0-SNAPSHOT.1748629362620 → 74.0.0-SNAPSHOT.1749036244810
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 +3 -0
- package/build/types/cmp/grid/GridModel.d.ts +3 -1
- package/build/types/cmp/grid/columns/Column.d.ts +7 -0
- package/build/types/svc/WebSocketService.d.ts +4 -0
- package/cmp/grid/GridModel.ts +3 -1
- package/cmp/grid/columns/Column.ts +12 -1
- package/package.json +1 -1
- package/svc/WebSocketService.ts +34 -9
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
### 🎁 New Features
|
|
10
10
|
* Added `ViewManagerModel.preserveUnsavedChanges` flag to opt-out of that behaviour.
|
|
11
11
|
* Added `PersistOptions.settleTime` to configure time to wait for state to settle before persisting.
|
|
12
|
+
* Support for gridcolumn level `onCellClicked` events.
|
|
12
13
|
|
|
13
14
|
### 🐞 Bug Fixes
|
|
14
15
|
* Improved `ViewManagerModel.settleTime` by delegating to individual `PersistenceProviders`.
|
|
15
16
|
* Fixed bug where grid column state could become unintentionally dirty when columns were hidden.
|
|
17
|
+
* Improved `WebsocketService` heartbeat detection to auto-reconnect when the socket reports as open
|
|
18
|
+
and heartbeats can be sent, but no heartbeat acknowledgements are being received from the server.
|
|
16
19
|
|
|
17
20
|
## v73.0.1 - 2025-05-19
|
|
18
21
|
|
|
@@ -120,7 +120,9 @@ export interface GridConfig {
|
|
|
120
120
|
*/
|
|
121
121
|
onRowDoubleClicked?: (e: RowDoubleClickedEvent) => void;
|
|
122
122
|
/**
|
|
123
|
-
* Callback when
|
|
123
|
+
* Callback when any cell on the grid is clicked - inspect the event to determine the column.
|
|
124
|
+
* Note that {@link ColumnSpec.onCellClicked} is a more targeted handler scoped to a single
|
|
125
|
+
* column, which might be more convenient when clicks on only one column are of interest.
|
|
124
126
|
*/
|
|
125
127
|
onCellClicked?: (e: CellClickedEvent) => void;
|
|
126
128
|
/**
|
|
@@ -4,6 +4,7 @@ import { FunctionComponent, ReactNode } from 'react';
|
|
|
4
4
|
import { GridModel } from '../GridModel';
|
|
5
5
|
import { ColumnCellClassFn, ColumnCellClassRuleFn, ColumnComparator, ColumnEditableFn, ColumnEditorFn, ColumnEditorProps, ColumnExcelFormatFn, ColumnExportValueFn, ColumnGetValueFn, ColumnHeaderClassFn, ColumnHeaderNameFn, ColumnRenderer, ColumnSetValueFn, ColumnSortSpec, ColumnSortValueFn, ColumnTooltipFn } from '../Types';
|
|
6
6
|
import type { ColDef } from '@xh/hoist/kit/ag-grid';
|
|
7
|
+
import { CellClickedEvent } from '@ag-grid-community/core';
|
|
7
8
|
export interface ColumnSpec {
|
|
8
9
|
/**
|
|
9
10
|
* Name of data store field to display within the column, or object containing properties
|
|
@@ -248,6 +249,11 @@ export interface ColumnSpec {
|
|
|
248
249
|
* many rows + multiple actions per row. Defaults to false;
|
|
249
250
|
*/
|
|
250
251
|
actionsShowOnHoverOnly?: boolean;
|
|
252
|
+
/**
|
|
253
|
+
* Callback when a cell within this column clicked.
|
|
254
|
+
* See also {@link GridConfig.onCellClicked}, called when any cell within the grid is clicked.
|
|
255
|
+
*/
|
|
256
|
+
onCellClicked?: (e: CellClickedEvent) => void;
|
|
251
257
|
/**
|
|
252
258
|
* "escape hatch" object to pass directly to Ag-Grid for desktop implementations. Note these
|
|
253
259
|
* options may be used / overwritten by the framework itself, and are not all guaranteed to be
|
|
@@ -337,6 +343,7 @@ export declare class Column {
|
|
|
337
343
|
actionsShowOnHoverOnly?: boolean;
|
|
338
344
|
fieldSpec: FieldSpec;
|
|
339
345
|
omit: Thunkable<boolean>;
|
|
346
|
+
onCellClicked?: (e: CellClickedEvent) => void;
|
|
340
347
|
gridModel: GridModel;
|
|
341
348
|
agOptions: ColDef;
|
|
342
349
|
appData: PlainObject;
|
|
@@ -45,6 +45,8 @@ export declare class WebSocketService extends HoistService {
|
|
|
45
45
|
private _timer;
|
|
46
46
|
private _socket;
|
|
47
47
|
private _subsByTopic;
|
|
48
|
+
private _lastHeartbeatSent;
|
|
49
|
+
private _lastHeartbeatReceived;
|
|
48
50
|
constructor();
|
|
49
51
|
initAsync(): Promise<void>;
|
|
50
52
|
/**
|
|
@@ -70,7 +72,9 @@ export declare class WebSocketService extends HoistService {
|
|
|
70
72
|
getFormattedTelemetry(): PlainObject;
|
|
71
73
|
private connect;
|
|
72
74
|
private disconnect;
|
|
75
|
+
private reconnect;
|
|
73
76
|
private heartbeatOrReconnect;
|
|
77
|
+
private get heartbeatWasUnacknowledged();
|
|
74
78
|
private onServerInstanceChange;
|
|
75
79
|
onOpen(ev: any): void;
|
|
76
80
|
onClose(ev: any): void;
|
package/cmp/grid/GridModel.ts
CHANGED
|
@@ -255,7 +255,9 @@ export interface GridConfig {
|
|
|
255
255
|
onRowDoubleClicked?: (e: RowDoubleClickedEvent) => void;
|
|
256
256
|
|
|
257
257
|
/**
|
|
258
|
-
* Callback when
|
|
258
|
+
* Callback when any cell on the grid is clicked - inspect the event to determine the column.
|
|
259
|
+
* Note that {@link ColumnSpec.onCellClicked} is a more targeted handler scoped to a single
|
|
260
|
+
* column, which might be more convenient when clicks on only one column are of interest.
|
|
259
261
|
*/
|
|
260
262
|
onCellClicked?: (e: CellClickedEvent) => void;
|
|
261
263
|
|
|
@@ -71,6 +71,7 @@ import type {
|
|
|
71
71
|
ValueGetterParams,
|
|
72
72
|
ValueSetterParams
|
|
73
73
|
} from '@xh/hoist/kit/ag-grid';
|
|
74
|
+
import {CellClickedEvent} from '@ag-grid-community/core';
|
|
74
75
|
|
|
75
76
|
export interface ColumnSpec {
|
|
76
77
|
/**
|
|
@@ -374,6 +375,12 @@ export interface ColumnSpec {
|
|
|
374
375
|
*/
|
|
375
376
|
actionsShowOnHoverOnly?: boolean;
|
|
376
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Callback when a cell within this column clicked.
|
|
380
|
+
* See also {@link GridConfig.onCellClicked}, called when any cell within the grid is clicked.
|
|
381
|
+
*/
|
|
382
|
+
onCellClicked?: (e: CellClickedEvent) => void;
|
|
383
|
+
|
|
377
384
|
/**
|
|
378
385
|
* "escape hatch" object to pass directly to Ag-Grid for desktop implementations. Note these
|
|
379
386
|
* options may be used / overwritten by the framework itself, and are not all guaranteed to be
|
|
@@ -479,6 +486,7 @@ export class Column {
|
|
|
479
486
|
actionsShowOnHoverOnly?: boolean;
|
|
480
487
|
fieldSpec: FieldSpec;
|
|
481
488
|
omit: Thunkable<boolean>;
|
|
489
|
+
onCellClicked?: (e: CellClickedEvent) => void;
|
|
482
490
|
|
|
483
491
|
gridModel: GridModel;
|
|
484
492
|
agOptions: ColDef;
|
|
@@ -551,6 +559,7 @@ export class Column {
|
|
|
551
559
|
actionsShowOnHoverOnly,
|
|
552
560
|
actions,
|
|
553
561
|
omit,
|
|
562
|
+
onCellClicked,
|
|
554
563
|
agOptions,
|
|
555
564
|
appData,
|
|
556
565
|
...rest
|
|
@@ -657,6 +666,7 @@ export class Column {
|
|
|
657
666
|
|
|
658
667
|
this.actions = actions;
|
|
659
668
|
this.actionsShowOnHoverOnly = actionsShowOnHoverOnly ?? false;
|
|
669
|
+
this.onCellClicked = onCellClicked;
|
|
660
670
|
|
|
661
671
|
this.gridModel = gridModel;
|
|
662
672
|
this.agOptions = agOptions ? clone(agOptions) : {};
|
|
@@ -758,7 +768,8 @@ export class Column {
|
|
|
758
768
|
if (event.shiftKey && event.key === 'Enter') return true;
|
|
759
769
|
|
|
760
770
|
return false;
|
|
761
|
-
}
|
|
771
|
+
},
|
|
772
|
+
onCellClicked: this.onCellClicked
|
|
762
773
|
};
|
|
763
774
|
|
|
764
775
|
// We will change this setter as needed to install the renderer in the proper location
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "74.0.0-SNAPSHOT.
|
|
3
|
+
"version": "74.0.0-SNAPSHOT.1749036244810",
|
|
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",
|
package/svc/WebSocketService.ts
CHANGED
|
@@ -70,6 +70,9 @@ export class WebSocketService extends HoistService {
|
|
|
70
70
|
private _socket: WebSocket;
|
|
71
71
|
private _subsByTopic: Record<string, WebSocketSubscription[]> = {};
|
|
72
72
|
|
|
73
|
+
private _lastHeartbeatSent: number = null;
|
|
74
|
+
private _lastHeartbeatReceived: number = null;
|
|
75
|
+
|
|
73
76
|
constructor() {
|
|
74
77
|
super();
|
|
75
78
|
makeObservable(this);
|
|
@@ -177,6 +180,10 @@ export class WebSocketService extends HoistService {
|
|
|
177
180
|
if (s === this._socket) this.onMessage(data);
|
|
178
181
|
};
|
|
179
182
|
this._socket = s;
|
|
183
|
+
|
|
184
|
+
// Reset heartbeat tracking - any prior values no longer relevant.
|
|
185
|
+
this._lastHeartbeatReceived = null;
|
|
186
|
+
this._lastHeartbeatSent = null;
|
|
180
187
|
} catch (e) {
|
|
181
188
|
this.logError('Failure creating WebSocket', e);
|
|
182
189
|
}
|
|
@@ -192,24 +199,41 @@ export class WebSocketService extends HoistService {
|
|
|
192
199
|
this.updateConnectedStatus();
|
|
193
200
|
}
|
|
194
201
|
|
|
202
|
+
private reconnect() {
|
|
203
|
+
this.disconnect();
|
|
204
|
+
this.connect();
|
|
205
|
+
}
|
|
206
|
+
|
|
195
207
|
private heartbeatOrReconnect() {
|
|
196
208
|
this.updateConnectedStatus();
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
this.logWarn(
|
|
209
|
+
|
|
210
|
+
// If there is a problem, attempt to reconnect and come back on the next cycle.
|
|
211
|
+
const {connected, heartbeatWasUnacknowledged} = this;
|
|
212
|
+
if (!connected || heartbeatWasUnacknowledged) {
|
|
213
|
+
this.logWarn(
|
|
214
|
+
`Heartbeat found ${!connected ? 'websocket not connected' : 'last heartbeat not acknowledged'} - attempting to reconnect...`
|
|
215
|
+
);
|
|
202
216
|
this.noteTelemetryEvent('heartbeatReconnectAttempt');
|
|
203
|
-
this.
|
|
204
|
-
|
|
217
|
+
this.reconnect();
|
|
218
|
+
return;
|
|
205
219
|
}
|
|
220
|
+
|
|
221
|
+
// If all looks OK, send a heartbeat message.
|
|
222
|
+
this.sendMessage({topic: this.HEARTBEAT_TOPIC, data: 'ping'});
|
|
223
|
+
this.noteTelemetryEvent('heartbeatSent');
|
|
224
|
+
this._lastHeartbeatSent = Date.now();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// We expect the server to respond immediately to every heartbeat. There will be a tiny window
|
|
228
|
+
// while the message is round-tripping, but that's not material to our check on HEARTBEAT_INTERVAL.
|
|
229
|
+
private get heartbeatWasUnacknowledged() {
|
|
230
|
+
return this._lastHeartbeatSent > this._lastHeartbeatReceived;
|
|
206
231
|
}
|
|
207
232
|
|
|
208
233
|
private onServerInstanceChange() {
|
|
209
234
|
this.logWarn('Server instance changed - attempting to connect to new instance.');
|
|
210
235
|
this.noteTelemetryEvent('instanceChangeReconnectAttempt');
|
|
211
|
-
this.
|
|
212
|
-
this.connect();
|
|
236
|
+
this.reconnect();
|
|
213
237
|
}
|
|
214
238
|
|
|
215
239
|
//------------------------
|
|
@@ -256,6 +280,7 @@ export class WebSocketService extends HoistService {
|
|
|
256
280
|
XH.clientHealthService.sendReportAsync();
|
|
257
281
|
break;
|
|
258
282
|
case this.HEARTBEAT_TOPIC:
|
|
283
|
+
this._lastHeartbeatReceived = Date.now();
|
|
259
284
|
this.noteTelemetryEvent('heartbeatReceived');
|
|
260
285
|
break;
|
|
261
286
|
}
|