@xh/hoist 68.0.0-SNAPSHOT.1726615045723 → 68.0.0-SNAPSHOT.1726671325631

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 CHANGED
@@ -6,6 +6,10 @@
6
6
 
7
7
  * Requires `hoist-core >= 21.1` for consolidated polling of Alert Banner updates (see below).
8
8
 
9
+ ### 🎁 New Features
10
+
11
+ * Added expand/collapse affordance in the left column header of ZoneGrids in tree mode.
12
+
9
13
  ### ⚙️ Technical
10
14
 
11
15
  * Updated Admin Console's Cluster tab to refresh more frequently.
@@ -27,22 +27,24 @@ export const AppComponent = hoistCmp({
27
27
  }
28
28
  });
29
29
 
30
- const tbar = hoistCmp.factory<AppModel>(({model}) =>
31
- appBar({
30
+ const tbar = hoistCmp.factory<AppModel>(({model}) => {
31
+ const primaryApp = model.getPrimaryAppCode();
32
+ return appBar({
32
33
  icon: Icon.gears({size: '2x', prefix: 'fal'}),
33
34
  leftItems: [tabSwitcher({testId: 'tab-switcher', enableOverflow: true})],
34
35
  rightItems: [
35
36
  button({
36
37
  icon: Icon.openExternal(),
37
- title: 'Open app...',
38
- onClick: () => window.open('/')
38
+ tooltip: `Open ${primaryApp}...`,
39
+ omit: !primaryApp,
40
+ onClick: () => model.openPrimaryApp()
39
41
  }),
40
- appBarSeparator()
42
+ appBarSeparator({omit: !primaryApp})
41
43
  ],
42
44
  appMenuButtonProps: {
43
45
  hideAdminItem: true,
44
46
  hideFeedbackItem: true,
45
47
  extraItems: model.getAppMenuButtonExtraItems()
46
48
  }
47
- })
48
- );
49
+ });
50
+ });
package/admin/AppModel.ts CHANGED
@@ -4,15 +4,16 @@
4
4
  *
5
5
  * Copyright © 2024 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {clusterTab} from '@xh/hoist/admin/tabs/cluster/ClusterTab';
7
8
  import {GridModel} from '@xh/hoist/cmp/grid';
8
9
  import {TabConfig, TabContainerModel} from '@xh/hoist/cmp/tab';
9
10
  import {HoistAppModel, managed, XH} from '@xh/hoist/core';
10
11
  import {Icon} from '@xh/hoist/icon';
12
+ import {without} from 'lodash';
11
13
  import {Route} from 'router5';
12
14
  import {activityTab} from './tabs/activity/ActivityTab';
13
15
  import {generalTab} from './tabs/general/GeneralTab';
14
16
  import {monitorTab} from './tabs/monitor/MonitorTab';
15
- import {clusterTab} from '@xh/hoist/admin/tabs/cluster/ClusterTab';
16
17
  import {userDataTab} from './tabs/userData/UserDataTab';
17
18
 
18
19
  export class AppModel extends HoistAppModel {
@@ -136,4 +137,14 @@ export class AppModel extends HoistAppModel {
136
137
  }
137
138
  ];
138
139
  }
140
+
141
+ /** Open the primary business-facing application, typically 'app'. */
142
+ openPrimaryApp() {
143
+ window.open(`/${this.getPrimaryAppCode()}`);
144
+ }
145
+
146
+ getPrimaryAppCode() {
147
+ const appCodes = without(XH.clientApps, XH.clientAppCode, 'mobile');
148
+ return appCodes.find(it => it === 'app') ?? appCodes[0];
149
+ }
139
150
  }
@@ -5,10 +5,13 @@
5
5
  * Copyright © 2024 Extremely Heavy Industries Inc.
6
6
  */
7
7
  import {RangeAggregator} from '@xh/hoist/admin/tabs/activity/aggregators/RangeAggregator';
8
+ import {badge} from '@xh/hoist/cmp/badge';
9
+ import {XH} from '@xh/hoist/core';
8
10
  import {Icon} from '@xh/hoist/icon';
9
11
  import {fmtDate, fmtSpan, numberRenderer} from '@xh/hoist/format';
10
12
  import * as Col from '@xh/hoist/cmp/grid/columns';
11
13
  import {ColumnSpec} from '@xh/hoist/cmp/grid/columns';
14
+ import copy from 'clipboard-copy';
12
15
 
13
16
  export const appEnvironment: ColumnSpec = {
14
17
  field: {
@@ -113,6 +116,7 @@ export const correlationId: ColumnSpec = {
113
116
  type: 'string',
114
117
  displayName: 'Correlation ID'
115
118
  },
119
+ renderer: badgeRenderer,
116
120
  width: 100
117
121
  };
118
122
 
@@ -121,8 +125,8 @@ export const error: ColumnSpec = {
121
125
  name: 'error',
122
126
  type: 'string'
123
127
  },
124
- flex: true,
125
- minWidth: 150,
128
+ width: 250,
129
+ autosizeMaxWidth: 400,
126
130
  renderer: e => fmtSpan(e, {className: 'xh-font-family-mono xh-font-size-small'})
127
131
  };
128
132
 
@@ -134,9 +138,8 @@ export const msg: ColumnSpec = {
134
138
  isDimension: true,
135
139
  aggregator: 'UNIQUE'
136
140
  },
137
- minWidth: 120,
138
- autosizeMaxWidth: 400,
139
- flex: true
141
+ width: 250,
142
+ autosizeMaxWidth: 400
140
143
  };
141
144
 
142
145
  export const url: ColumnSpec = {
@@ -156,6 +159,7 @@ export const instance: ColumnSpec = {
156
159
  isDimension: true,
157
160
  aggregator: 'UNIQUE'
158
161
  },
162
+ renderer: badgeRenderer,
159
163
  width: 100
160
164
  };
161
165
 
@@ -230,3 +234,20 @@ function dayRangeComparator(rangeA, rangeB, sortDir, abs, {defaultComparator}) {
230
234
 
231
235
  return defaultComparator(maxA, maxB);
232
236
  }
237
+
238
+ function badgeRenderer(v) {
239
+ return v
240
+ ? badge({
241
+ item: v,
242
+ className: 'xh-font-family-mono xh-title-tip',
243
+ title: 'Double-click to copy',
244
+ onDoubleClick: () => {
245
+ copy(v);
246
+ XH.toast({
247
+ icon: Icon.copy(),
248
+ message: `Copied ${v}`
249
+ });
250
+ }
251
+ })
252
+ : '-';
253
+ }
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * Copyright © 2024 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {correlationId, instance} from '@xh/hoist/admin/columns';
7
8
  import {form} from '@xh/hoist/cmp/form';
8
9
  import {a, div, h3, hframe, span, vbox} from '@xh/hoist/cmp/layout';
9
10
  import {hoistCmp} from '@xh/hoist/core';
@@ -49,10 +50,6 @@ export const clientErrorDetail = hoistCmp.factory<ClientErrorsModel>(({model}) =
49
50
  return span(username, impSpan);
50
51
  }
51
52
  }),
52
- formField({
53
- field: 'dateCreated',
54
- readonlyRenderer: v => fmtDateTimeSec(v)
55
- }),
56
53
  formField({
57
54
  field: 'appVersion',
58
55
  readonlyRenderer: appVersion => {
@@ -65,12 +62,22 @@ export const clientErrorDetail = hoistCmp.factory<ClientErrorsModel>(({model}) =
65
62
  field: 'userAlerted',
66
63
  label: 'User Alerted?'
67
64
  }),
68
- formField({field: 'id'}),
69
- formField({field: 'correlationId'}),
70
65
  formField({
71
66
  field: 'url',
72
67
  readonlyRenderer: hyperlinkVal
73
68
  }),
69
+ formField({
70
+ field: 'instance',
71
+ readonlyRenderer: v => instance.renderer(v, null)
72
+ }),
73
+ formField({
74
+ field: 'correlationId',
75
+ readonlyRenderer: v => correlationId.renderer(v, null)
76
+ }),
77
+ formField({
78
+ field: 'dateCreated',
79
+ readonlyRenderer: v => fmtDateTimeSec(v)
80
+ }),
74
81
  h3(Icon.desktop(), 'Device / Browser'),
75
82
  formField({field: 'device'}),
76
83
  formField({field: 'browser'}),
@@ -115,4 +122,4 @@ export const clientErrorDetail = hoistCmp.factory<ClientErrorsModel>(({model}) =
115
122
 
116
123
  const valOrNa = v => (!isNil(v) ? v.toString() : naSpan());
117
124
  const naSpan = () => span({item: 'N/A', className: 'xh-text-color-muted'});
118
- const hyperlinkVal = v => a({href: v, item: v, target: '_blank'});
125
+ const hyperlinkVal = v => (v ? a({href: v, item: v, target: '_blank'}) : '-');
@@ -52,9 +52,10 @@ export class ClientErrorsModel extends HoistModel {
52
52
  columns: [
53
53
  {...Col.userMessageFlag},
54
54
  {...Col.userAlertedFlag},
55
- {...Col.entryId, hidden},
56
55
  {...Col.impersonatingFlag},
56
+ {...Col.entryId, hidden},
57
57
  {...Col.username},
58
+ {...Col.impersonating, hidden},
58
59
  {...Col.browser},
59
60
  {...Col.device},
60
61
  {...Col.userAgent, hidden},
@@ -63,11 +64,10 @@ export class ClientErrorsModel extends HoistModel {
63
64
  {...Col.msg, displayName: 'User Message', hidden},
64
65
  {...Col.error, hidden},
65
66
  {...Col.url},
67
+ {...Col.correlationId},
66
68
  {...Col.instance, hidden},
67
69
  {...Col.day},
68
- {...Col.dateCreatedWithSec, displayName: 'Timestamp'},
69
- {...Col.impersonating, hidden},
70
- {...Col.correlationId}
70
+ {...Col.dateCreatedWithSec, displayName: 'Timestamp'}
71
71
  ]
72
72
  });
73
73
 
@@ -49,17 +49,17 @@ export class ActivityDetailModel extends HoistModel {
49
49
  {...Col.impersonating, hidden},
50
50
  {...Col.category},
51
51
  {...Col.msg},
52
- {...Col.url},
53
- {...Col.instance, hidden},
52
+ {...Col.browser},
53
+ {...Col.device},
54
+ {...Col.userAgent, hidden},
54
55
  {...Col.appVersion},
55
56
  {...Col.appEnvironment, hidden},
56
57
  {...Col.data, hidden},
57
- {...Col.device},
58
- {...Col.browser},
59
- {...Col.userAgent, hidden},
58
+ {...Col.url},
59
+ {...Col.correlationId},
60
+ {...Col.instance, hidden},
60
61
  {...Col.elapsed},
61
- {...Col.dateCreatedWithSec, displayName: 'Timestamp'},
62
- {...Col.correlationId}
62
+ {...Col.dateCreatedWithSec, displayName: 'Timestamp'}
63
63
  ]
64
64
  });
65
65
 
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * Copyright © 2024 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {correlationId, instance} from '@xh/hoist/admin/columns';
7
8
  import {form} from '@xh/hoist/cmp/form';
8
9
  import {grid, gridCountLabel} from '@xh/hoist/cmp/grid';
9
10
  import {a, div, filler, h3, hframe, placeholder, span} from '@xh/hoist/cmp/layout';
@@ -81,6 +82,7 @@ const detailRecForm = hoistCmp.factory<ActivityDetailModel>(({model}) => {
81
82
  }
82
83
  }),
83
84
  formField({field: 'category'}),
85
+ formField({field: 'msg'}),
84
86
  formField({
85
87
  field: 'appVersion',
86
88
  readonlyRenderer: appVersion => {
@@ -89,10 +91,17 @@ const detailRecForm = hoistCmp.factory<ActivityDetailModel>(({model}) => {
89
91
  return `${appVersion} (${appEnvironment})`;
90
92
  }
91
93
  }),
92
- formField({field: 'msg'}),
93
94
  formField({
94
- field: 'dateCreated',
95
- readonlyRenderer: dateTimeSecRenderer({})
95
+ field: 'url',
96
+ readonlyRenderer: hyperlinkVal
97
+ }),
98
+ formField({
99
+ field: 'instance',
100
+ readonlyRenderer: v => instance.renderer(v, null)
101
+ }),
102
+ formField({
103
+ field: 'correlationId',
104
+ readonlyRenderer: v => correlationId.renderer(v, null)
96
105
  }),
97
106
  formField({
98
107
  field: 'elapsed',
@@ -102,11 +111,9 @@ const detailRecForm = hoistCmp.factory<ActivityDetailModel>(({model}) => {
102
111
  formatConfig: {thousandSeparated: false, mantissa: 0}
103
112
  })
104
113
  }),
105
- formField({field: 'id'}),
106
- formField({field: 'correlationId'}),
107
114
  formField({
108
- field: 'url',
109
- readonlyRenderer: hyperlinkVal
115
+ field: 'dateCreated',
116
+ readonlyRenderer: dateTimeSecRenderer({})
110
117
  }),
111
118
  h3(Icon.desktop(), 'Device / Browser'),
112
119
  formField({field: 'device'}),
@@ -136,4 +143,4 @@ const additionalDataJsonInput = hoistCmp.factory<ActivityDetailModel>(({model})
136
143
 
137
144
  const valOrNa = v => (v != null ? v : naSpan());
138
145
  const naSpan = () => span({item: 'N/A', className: 'xh-text-color-muted'});
139
- const hyperlinkVal = v => a({href: v, item: v, target: '_blank'});
146
+ const hyperlinkVal = v => (v ? a({href: v, item: v, target: '_blank'}) : '-');
@@ -10,4 +10,7 @@ export declare class AppModel extends HoistAppModel {
10
10
  getAppMenuButtonExtraItems(): any[];
11
11
  getTabRoutes(): Route[];
12
12
  createTabs(): TabConfig[];
13
+ /** Open the primary business-facing application, typically 'app'. */
14
+ openPrimaryApp(): void;
15
+ getPrimaryAppCode(): string;
13
16
  }
@@ -51,6 +51,7 @@ import {
51
51
  } from '@xh/hoist/data';
52
52
  import {ColChooserModel as DesktopColChooserModel} from '@xh/hoist/dynamics/desktop';
53
53
  import {ColChooserModel as MobileColChooserModel} from '@xh/hoist/dynamics/mobile';
54
+ import {Icon} from '@xh/hoist/icon';
54
55
  import {action, bindable, makeObservable, observable, when} from '@xh/hoist/mobx';
55
56
  import {wait, waitFor} from '@xh/hoist/promise';
56
57
  import {ExportOptions} from '@xh/hoist/svc/GridExportService';
@@ -635,6 +636,7 @@ export class GridModel extends HoistModel {
635
636
  message: this.restoreDefaultsWarning,
636
637
  confirmProps: {
637
638
  text: 'Yes, restore defaults',
639
+ icon: Icon.reset(),
638
640
  intent: 'primary'
639
641
  }
640
642
  });
@@ -1103,6 +1103,6 @@ export class Column {
1103
1103
 
1104
1104
  return isFunction(sortValue)
1105
1105
  ? sortValue(v, {record, column: this, gridModel})
1106
- : record?.data[sortValue] ?? v;
1106
+ : (record?.data[sortValue] ?? v);
1107
1107
  }
1108
1108
  }
@@ -386,6 +386,7 @@ export class ZoneGridModel extends HoistModel {
386
386
  message: this.restoreDefaultsWarning,
387
387
  confirmProps: {
388
388
  text: 'Yes, restore defaults',
389
+ icon: Icon.reset(),
389
390
  intent: 'primary'
390
391
  }
391
392
  });
@@ -583,7 +584,7 @@ export class ZoneGridModel extends HoistModel {
583
584
  const colId = it.field;
584
585
  subFields.push({
585
586
  colId,
586
- label: it.showLabel ? labelRenderers[colId] ?? true : false,
587
+ label: it.showLabel ? (labelRenderers[colId] ?? true) : false,
587
588
  position: 'top'
588
589
  });
589
590
  });
@@ -591,7 +592,7 @@ export class ZoneGridModel extends HoistModel {
591
592
  const colId = it.field;
592
593
  subFields.push({
593
594
  colId,
594
- label: it.showLabel ? labelRenderers[colId] ?? true : false,
595
+ label: it.showLabel ? (labelRenderers[colId] ?? true) : false,
595
596
  position: 'bottom'
596
597
  });
597
598
  });
@@ -608,6 +609,7 @@ export class ZoneGridModel extends HoistModel {
608
609
  // Controlled properties
609
610
  field: isLeft ? 'left_column' : 'right_column',
610
611
  align: isLeft ? 'left' : 'right',
612
+ isTreeColumn: gridModel?.treeMode && isLeft,
611
613
  flex: overrideSpec.width ? null : isLeft ? 2 : 1,
612
614
  renderer: (value, context) => zoneGridRenderer(value, context, isLeft),
613
615
  rendererIsComplex: true,
@@ -51,6 +51,7 @@ export const [RestoreDefaultsButton, restoreDefaultsButton] =
51
51
  onConfirm: () => XH.restoreDefaultsAsync(),
52
52
  confirmProps: {
53
53
  text: 'Yes, restore defaults',
54
+ icon: Icon.reset(),
54
55
  intent: 'primary'
55
56
  }
56
57
  });
@@ -49,6 +49,7 @@ export const [RestoreDefaultsButton, restoreDefaultsButton] =
49
49
  onConfirm: () => XH.restoreDefaultsAsync(),
50
50
  confirmProps: {
51
51
  text: 'Yes, restore defaults',
52
+ icon: Icon.reset(),
52
53
  intent: 'primary'
53
54
  }
54
55
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "68.0.0-SNAPSHOT.1726615045723",
3
+ "version": "68.0.0-SNAPSHOT.1726671325631",
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",