@xh/hoist 86.2.0 → 86.3.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/.gitattributes +4 -0
- package/CHANGELOG.md +58 -0
- package/admin/AppModel.ts +3 -4
- package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +56 -6
- package/admin/tabs/activity/tracking/ActivityTrackingPanel.ts +9 -26
- package/admin/tabs/clients/ClientsModel.ts +4 -1
- package/admin/tabs/clients/activity/ClientDetailModel.ts +5 -1
- package/admin/tabs/userData/roles/RolePanel.ts +2 -2
- package/appcontainer/RouterModel.ts +6 -6
- package/build/types/admin/AppModel.d.ts +3 -4
- package/build/types/admin/tabs/activity/tracking/ActivityTrackingModel.d.ts +14 -3
- package/build/types/admin/tabs/clients/ClientsModel.d.ts +3 -0
- package/build/types/appcontainer/RouterModel.d.ts +5 -5
- package/build/types/cmp/grid/Grid.d.ts +2 -1
- package/build/types/cmp/grid/GridModel.d.ts +2 -2
- package/build/types/cmp/grid/Types.d.ts +8 -1
- package/build/types/cmp/input/SegmentedControlOption.d.ts +12 -0
- package/build/types/core/HoistAppModel.d.ts +2 -3
- package/build/types/core/types/Types.d.ts +12 -0
- package/build/types/desktop/cmp/filter/FilterChooser.d.ts +5 -0
- package/build/types/desktop/cmp/filter/PopoverFilterChooser.d.ts +3 -1
- package/build/types/desktop/cmp/grid/impl/colchooser/ColChooserModel.d.ts +2 -2
- package/build/types/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomRowModel.d.ts +9 -1
- package/build/types/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomTabModel.d.ts +8 -2
- package/build/types/desktop/cmp/input/Select.d.ts +8 -0
- package/build/types/kit/react-dropzone/index.d.ts +1 -0
- package/build/types/kit/swiper/index.d.ts +4 -3
- package/build/types/mobile/cmp/grid/impl/ColChooserModel.d.ts +2 -2
- package/build/types/mobile/cmp/input/Select.d.ts +8 -0
- package/cmp/grid/Grid.ts +10 -2
- package/cmp/grid/GridModel.ts +4 -3
- package/cmp/grid/Types.ts +9 -1
- package/cmp/grid/impl/MenuSupport.ts +1 -1
- package/cmp/input/SegmentedControlOption.ts +14 -0
- package/cmp/store/impl/StoreFilterFieldImplModel.ts +5 -6
- package/core/HoistAppModel.ts +2 -3
- package/core/types/Types.ts +14 -5
- package/desktop/cmp/filechooser/FileChooser.ts +6 -3
- package/desktop/cmp/filechooser/FileChooserModel.ts +12 -5
- package/desktop/cmp/filter/FilterChooser.scss +41 -0
- package/desktop/cmp/filter/FilterChooser.ts +147 -7
- package/desktop/cmp/filter/PopoverFilterChooser.ts +7 -84
- package/desktop/cmp/grid/editors/impl/InlineEditorModel.ts +6 -1
- package/desktop/cmp/grid/find/impl/GridFindFieldImplModel.ts +3 -5
- package/desktop/cmp/grid/impl/colchooser/ColChooserModel.ts +2 -2
- package/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomRow.ts +12 -12
- package/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomRowModel.ts +18 -0
- package/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomTab.scss +5 -0
- package/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomTab.ts +13 -2
- package/desktop/cmp/grid/impl/filter/headerfilter/custom/CustomTabModel.ts +97 -14
- package/desktop/cmp/input/SegmentedControl.ts +7 -3
- package/desktop/cmp/input/Select.ts +24 -16
- package/docs/routing.md +4 -2
- package/docs/upgrade-notes/v86-upgrade-notes.md +73 -8
- package/kit/react-dropzone/index.ts +1 -0
- package/mcp/README.md +12 -0
- package/mcp/data/ts-registry.spec.ts +156 -0
- package/mcp/data/ts-registry.ts +13 -6
- package/mcp/formatters/typescript.ts +7 -3
- package/mcp/util/paths.ts +21 -0
- package/mobile/cmp/grid/impl/ColChooserModel.ts +2 -2
- package/mobile/cmp/input/SegmentedControl.ts +7 -3
- package/mobile/cmp/input/Select.ts +21 -15
- package/mobx/README.md +2 -1
- package/package.json +5 -5
- package/desktop/cmp/filter/PopoverFilterChooser.scss +0 -42
package/.gitattributes
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 86.3.0 - 2026-07-10
|
|
4
|
+
|
|
5
|
+
### 🎁 New Features
|
|
6
|
+
|
|
7
|
+
* `Select` now accepts a `generateOptionFn` prop to resolve an option for a selected value that is
|
|
8
|
+
not present in the current options list (e.g. with `queryFn`-based selects or readonly forms),
|
|
9
|
+
ensuring such values render with their proper label rather than falling back to the raw value.
|
|
10
|
+
* `SegmentedControl` options (desktop and mobile) now accept a `testId`, emitted on the option's
|
|
11
|
+
rendered button as `data-testid` for E2E targeting. If an option omits its own `testId` but the
|
|
12
|
+
control has one, an id is auto-derived as `${controlTestId}-${value}` - restoring parity with
|
|
13
|
+
the legacy `ButtonGroupInput` test-hook pattern for apps migrating between the two.
|
|
14
|
+
|
|
15
|
+
### 🐞 Bug Fixes
|
|
16
|
+
|
|
17
|
+
* Fixed grid columns configured as `hidden` becoming visible after being grouped and then
|
|
18
|
+
ungrouped. `GridModel` now re-asserts each column's configured visibility whenever `groupBy`
|
|
19
|
+
changes, keeping AG Grid's column state in sync with `columnState`.
|
|
20
|
+
* Fixed `StoreFilterField` and grid Find so an active quick-filter or find query no longer returns
|
|
21
|
+
different results when the grid's `groupBy` changes.
|
|
22
|
+
* Fixed inline grid cell editors to reliably commit their value when editing ends, including popup
|
|
23
|
+
editors (e.g. `textAreaEditor`) within a dialog, which previously dropped edits on Enter or
|
|
24
|
+
click-away.
|
|
25
|
+
* Fixed `Select` to correctly handle non-primitive (object) values: selected-option matching and
|
|
26
|
+
async query de-duplication now use deep equality, so object values no longer render as
|
|
27
|
+
`[object Object]` or collide with one another.
|
|
28
|
+
* Hardened the grid column filter's Custom tab against filters it previously mishandled -
|
|
29
|
+
multi-value clauses are now expanded into editable rows and recombined on commit, and filters it
|
|
30
|
+
cannot represent are left untouched rather than corrupted.
|
|
31
|
+
* Fixed `FilterChooser` popover mode (formerly `PopoverFilterChooser`) so its collapsed control no
|
|
32
|
+
longer disappears when opened - it now always occupies its place in the layout, so surrounding
|
|
33
|
+
elements no longer shift. Its clear and favorites controls also respond to a single click rather
|
|
34
|
+
than requiring the popover to be opened first. This mode is now enabled more naturally via
|
|
35
|
+
a new option `filterChooser({popover: true})`, deprecating `PopoverFilterChooser`, which remains
|
|
36
|
+
as a thin alias.
|
|
37
|
+
* Fixed "not a valid MIME type" console warnings from `FileChooser`. Accepted extensions are now
|
|
38
|
+
passed under a dummy MIME type key, silencing the warnings while continuing to filter selected
|
|
39
|
+
files by extension.
|
|
40
|
+
|
|
41
|
+
### ⚙️ Typescript API Adjustments
|
|
42
|
+
|
|
43
|
+
* Retyped `GridModel.colChooserModel` as the new cross-platform `IColChooserModel` interface,
|
|
44
|
+
replacing the bare `HoistModel` type and exposing `isOpen`, `open()`, and `close()` directly.
|
|
45
|
+
* Added the exported `HoistRoute` type - Router5's `Route` extended with Hoist's `omit` key - and
|
|
46
|
+
retyped `HoistAppModel.getRoutes()` to return it, so declarative route exclusion (e.g.
|
|
47
|
+
`omit: !XH.getUser().isHoistAdmin`) now type-checks without a cast.
|
|
48
|
+
|
|
49
|
+
### 🤖 AI Docs + Tooling
|
|
50
|
+
|
|
51
|
+
* Fixed the MCP server and `hoist-ts` CLI TypeScript symbol tools (`search`, `symbol`, `members`)
|
|
52
|
+
returning no results on Windows, where a path-separator mismatch left the symbol index empty.
|
|
53
|
+
Path handling is now normalized so the developer tools work on Windows as well as macOS/Linux.
|
|
54
|
+
|
|
55
|
+
### 📚 Libraries
|
|
56
|
+
|
|
57
|
+
* @auth0/auth0-spa-js `2.21 → 2.23`
|
|
58
|
+
* @azure/msal-browser `5.14 → 5.16`
|
|
59
|
+
* swiper `12.1 -> 14.0`
|
|
60
|
+
|
|
3
61
|
## 86.2.0 - 2026-06-25
|
|
4
62
|
|
|
5
63
|
### 🎁 New Features
|
package/admin/AppModel.ts
CHANGED
|
@@ -7,10 +7,9 @@
|
|
|
7
7
|
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
8
8
|
import {TabConfig, TabContainerModel} from '@xh/hoist/cmp/tab';
|
|
9
9
|
import {ViewManagerModel} from '@xh/hoist/cmp/viewmanager';
|
|
10
|
-
import {HoistAppModel, InitContext, XH} from '@xh/hoist/core';
|
|
10
|
+
import {HoistAppModel, HoistRoute, InitContext, XH} from '@xh/hoist/core';
|
|
11
11
|
import {Icon} from '@xh/hoist/icon';
|
|
12
12
|
import {without} from 'lodash';
|
|
13
|
-
import {Route} from 'router5';
|
|
14
13
|
import {activityTrackingPanel} from './tabs/activity/tracking/ActivityTrackingPanel';
|
|
15
14
|
import {clientsPanel} from './tabs/clients/ClientsPanel';
|
|
16
15
|
import {monitorTab} from './tabs/monitor/MonitorTab';
|
|
@@ -49,7 +48,7 @@ export class AppModel extends HoistAppModel {
|
|
|
49
48
|
await super.initAsync(ctx);
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
override getRoutes():
|
|
51
|
+
override getRoutes(): HoistRoute[] {
|
|
53
52
|
return [
|
|
54
53
|
{
|
|
55
54
|
name: 'default',
|
|
@@ -66,7 +65,7 @@ export class AppModel extends HoistAppModel {
|
|
|
66
65
|
return [];
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
getTabRoutes():
|
|
68
|
+
getTabRoutes(): HoistRoute[] {
|
|
70
69
|
return [
|
|
71
70
|
{
|
|
72
71
|
name: 'general',
|
|
@@ -15,7 +15,17 @@ import {FilterChooserModel} from '@xh/hoist/cmp/filter';
|
|
|
15
15
|
import {FormModel} from '@xh/hoist/cmp/form';
|
|
16
16
|
import {ColumnRenderer, ColumnSpec, GridModel, TreeStyle} from '@xh/hoist/cmp/grid';
|
|
17
17
|
import {GroupingChooserModel} from '@xh/hoist/cmp/grouping';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
HoistModel,
|
|
20
|
+
LoadSpec,
|
|
21
|
+
LocalDateUnit,
|
|
22
|
+
managed,
|
|
23
|
+
PersistableState,
|
|
24
|
+
PersistenceProvider,
|
|
25
|
+
persistOptions,
|
|
26
|
+
PlainObject,
|
|
27
|
+
XH
|
|
28
|
+
} from '@xh/hoist/core';
|
|
19
29
|
import {Cube, CubeFieldSpec, FieldSpec, ViewRowData} from '@xh/hoist/data';
|
|
20
30
|
import {dateRenderer, dateTimeSecRenderer, numberRenderer} from '@xh/hoist/format';
|
|
21
31
|
import {action, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
@@ -24,6 +34,23 @@ import {compact, get, isEmpty, isEqual, round} from 'lodash';
|
|
|
24
34
|
import moment from 'moment';
|
|
25
35
|
import {ActivityDetailProvider} from './detail/ActivityDetailModel';
|
|
26
36
|
|
|
37
|
+
/**
|
|
38
|
+
* A relative time interval (e.g. 7 days) ending on the current app day. Drives the quick-select
|
|
39
|
+
* buttons and the persisted time period, which rolls forward to end on today on restore.
|
|
40
|
+
*/
|
|
41
|
+
export interface ActivityInterval {
|
|
42
|
+
value: number;
|
|
43
|
+
unit: LocalDateUnit;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Quick-select intervals offered in the toolbar - labels are derived as `${value}${unit[0]}`. */
|
|
47
|
+
export const INTERVALS: ActivityInterval[] = [
|
|
48
|
+
{value: 6, unit: 'months'},
|
|
49
|
+
{value: 1, unit: 'months'},
|
|
50
|
+
{value: 7, unit: 'days'},
|
|
51
|
+
{value: 1, unit: 'days'}
|
|
52
|
+
];
|
|
53
|
+
|
|
27
54
|
export class ActivityTrackingModel extends HoistModel implements ActivityDetailProvider {
|
|
28
55
|
override telemetryPrefix = 'xh.client.admin.tracking';
|
|
29
56
|
|
|
@@ -115,6 +142,7 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
|
|
|
115
142
|
this.markPersist('showFilterChooser');
|
|
116
143
|
|
|
117
144
|
this.formModel = this.createQueryFormModel();
|
|
145
|
+
this.setupPeriodPersistence();
|
|
118
146
|
|
|
119
147
|
this.dataFieldsEditorModel = new DataFieldsEditorModel(this);
|
|
120
148
|
this.markPersist('dataFields');
|
|
@@ -203,17 +231,17 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
|
|
|
203
231
|
endDay.setValue(newEnd);
|
|
204
232
|
}
|
|
205
233
|
|
|
206
|
-
// Set the start date by taking the end date and pushing back
|
|
234
|
+
// Set the start date by taking the end date and pushing back the interval - then pushing
|
|
207
235
|
// forward one day as the day range query is inclusive.
|
|
208
|
-
adjustStartDate(
|
|
236
|
+
adjustStartDate(interval: ActivityInterval) {
|
|
209
237
|
this.formModel.setValues({
|
|
210
|
-
startDay: this.endDay.subtract(value, unit).nextDay()
|
|
238
|
+
startDay: this.endDay.subtract(interval.value, interval.unit).nextDay()
|
|
211
239
|
});
|
|
212
240
|
}
|
|
213
241
|
|
|
214
|
-
isInterval(
|
|
242
|
+
isInterval(interval: ActivityInterval) {
|
|
215
243
|
const {startDay, endDay} = this.formModel.values;
|
|
216
|
-
return startDay === endDay.subtract(value, unit).nextDay();
|
|
244
|
+
return startDay === endDay.subtract(interval.value, interval.unit).nextDay();
|
|
217
245
|
}
|
|
218
246
|
|
|
219
247
|
getDisplayName(fieldName: string) {
|
|
@@ -234,6 +262,28 @@ export class ActivityTrackingModel extends HoistModel implements ActivityDetailP
|
|
|
234
262
|
});
|
|
235
263
|
}
|
|
236
264
|
|
|
265
|
+
// Persist the selected time period as part of the active view, if endDay is currentDay
|
|
266
|
+
private setupPeriodPersistence() {
|
|
267
|
+
PersistenceProvider.create<ActivityInterval>({
|
|
268
|
+
persistOptions: persistOptions({path: 'queryPeriod'}, this.persistWith),
|
|
269
|
+
owner: this,
|
|
270
|
+
target: {
|
|
271
|
+
getPersistableState: () => {
|
|
272
|
+
const currDay = this.endDay === LocalDate.currentAppDay(),
|
|
273
|
+
interval = currDay ? INTERVALS.find(it => this.isInterval(it)) : null;
|
|
274
|
+
return new PersistableState(interval ?? null);
|
|
275
|
+
},
|
|
276
|
+
setPersistableState: ({value}) => {
|
|
277
|
+
if (value) {
|
|
278
|
+
const endDay = LocalDate.currentAppDay();
|
|
279
|
+
this.formModel.setValues({endDay});
|
|
280
|
+
this.adjustStartDate(value);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
237
287
|
private async loadGridAsync() {
|
|
238
288
|
const {cube, gridModel, dimensions} = this,
|
|
239
289
|
data = cube.executeQuery({
|
|
@@ -11,7 +11,7 @@ import {grid} from '@xh/hoist/cmp/grid';
|
|
|
11
11
|
import {div, filler, hframe} from '@xh/hoist/cmp/layout';
|
|
12
12
|
import {creates, hoistCmp} from '@xh/hoist/core';
|
|
13
13
|
import {button, buttonGroup, colChooserButton, exportButton} from '@xh/hoist/desktop/cmp/button';
|
|
14
|
-
import {
|
|
14
|
+
import {filterChooser} from '@xh/hoist/desktop/cmp/filter';
|
|
15
15
|
import {formField} from '@xh/hoist/desktop/cmp/form';
|
|
16
16
|
import {groupingChooser} from '@xh/hoist/desktop/cmp/grouping';
|
|
17
17
|
import {dateInput, DateInputProps, select} from '@xh/hoist/desktop/cmp/input';
|
|
@@ -20,7 +20,7 @@ import {toolbar, toolbarSep} from '@xh/hoist/desktop/cmp/toolbar';
|
|
|
20
20
|
import {viewManager} from '@xh/hoist/desktop/cmp/viewmanager';
|
|
21
21
|
import {Icon} from '@xh/hoist/icon';
|
|
22
22
|
import {LocalDate} from '@xh/hoist/utils/datetime';
|
|
23
|
-
import {ActivityTrackingModel} from './ActivityTrackingModel';
|
|
23
|
+
import {INTERVALS, ActivityTrackingModel} from './ActivityTrackingModel';
|
|
24
24
|
import {aggChartPanel} from '@xh/hoist/admin/tabs/activity/tracking/chart/AggChartPanel';
|
|
25
25
|
import {activityDetailView} from './detail/ActivityDetailView';
|
|
26
26
|
import './ActivityTracking.scss';
|
|
@@ -75,32 +75,14 @@ const tbar = hoistCmp.factory<ActivityTrackingModel>(({model}) => {
|
|
|
75
75
|
disabled: model.endDay >= LocalDate.currentAppDay()
|
|
76
76
|
}),
|
|
77
77
|
buttonGroup({
|
|
78
|
-
items:
|
|
78
|
+
items: INTERVALS.map(interval =>
|
|
79
79
|
button({
|
|
80
|
-
text:
|
|
81
|
-
onClick: () => model.adjustStartDate(
|
|
82
|
-
active: model.isInterval(
|
|
83
|
-
...dateBtn
|
|
84
|
-
}),
|
|
85
|
-
button({
|
|
86
|
-
text: '1m',
|
|
87
|
-
onClick: () => model.adjustStartDate(1, 'months'),
|
|
88
|
-
active: model.isInterval(1, 'months'),
|
|
89
|
-
...dateBtn
|
|
90
|
-
}),
|
|
91
|
-
button({
|
|
92
|
-
text: '7d',
|
|
93
|
-
onClick: () => model.adjustStartDate(7, 'days'),
|
|
94
|
-
active: model.isInterval(7, 'days'),
|
|
95
|
-
...dateBtn
|
|
96
|
-
}),
|
|
97
|
-
button({
|
|
98
|
-
text: '1d',
|
|
99
|
-
onClick: () => model.adjustStartDate(1, 'days'),
|
|
100
|
-
active: model.isInterval(1, 'days'),
|
|
80
|
+
text: `${interval.value}${interval.unit[0]}`,
|
|
81
|
+
onClick: () => model.adjustStartDate(interval),
|
|
82
|
+
active: model.isInterval(interval),
|
|
101
83
|
...dateBtn
|
|
102
84
|
})
|
|
103
|
-
|
|
85
|
+
)
|
|
104
86
|
}),
|
|
105
87
|
toolbarSep(),
|
|
106
88
|
filterChooserToggleButton(),
|
|
@@ -137,7 +119,8 @@ const filterChooserToggleButton = hoistCmp.factory<ActivityTrackingModel>(({mode
|
|
|
137
119
|
const filterBar = hoistCmp.factory<ActivityTrackingModel>(({model}) => {
|
|
138
120
|
return model.showFilterChooser
|
|
139
121
|
? toolbar(
|
|
140
|
-
|
|
122
|
+
filterChooser({
|
|
123
|
+
popover: true,
|
|
141
124
|
flex: 1,
|
|
142
125
|
enableClear: true
|
|
143
126
|
})
|
|
@@ -9,7 +9,7 @@ import {AppModel} from '@xh/hoist/admin/AppModel';
|
|
|
9
9
|
import * as Col from '@xh/hoist/admin/columns';
|
|
10
10
|
import {GridModel} from '@xh/hoist/cmp/grid';
|
|
11
11
|
import {div, p} from '@xh/hoist/cmp/layout';
|
|
12
|
-
import {LoadSpec, managed, XH} from '@xh/hoist/core';
|
|
12
|
+
import {LoadSpec, managed, persistOptions, XH} from '@xh/hoist/core';
|
|
13
13
|
import {RecordActionSpec, StoreRecord} from '@xh/hoist/data';
|
|
14
14
|
import {textInput} from '@xh/hoist/desktop/cmp/input';
|
|
15
15
|
import {Icon} from '@xh/hoist/icon';
|
|
@@ -23,6 +23,8 @@ import {BaseAdminTabModel} from '@xh/hoist/admin/tabs/BaseAdminTabModel';
|
|
|
23
23
|
export class ClientsModel extends BaseAdminTabModel {
|
|
24
24
|
override telemetryPrefix = 'xh.client.admin.clients';
|
|
25
25
|
|
|
26
|
+
override persistWith = {localStorageKey: 'xhAdminClientsState'};
|
|
27
|
+
|
|
26
28
|
@observable
|
|
27
29
|
lastRefresh: number;
|
|
28
30
|
|
|
@@ -150,6 +152,7 @@ export class ClientsModel extends BaseAdminTabModel {
|
|
|
150
152
|
return new GridModel({
|
|
151
153
|
emptyText: 'No clients connected.',
|
|
152
154
|
groupBy: this.groupBy,
|
|
155
|
+
persistWith: persistOptions(this.persistWith, {path: 'mainGrid'}),
|
|
153
156
|
colChooserModel: true,
|
|
154
157
|
enableExport: true,
|
|
155
158
|
selModel: 'multiple',
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {ClientsModel} from '../ClientsModel';
|
|
8
8
|
import {ColumnSpec} from '@xh/hoist/cmp/grid';
|
|
9
|
-
import {HoistModel, LoadSpec, lookup, PlainObject, XH} from '@xh/hoist/core';
|
|
9
|
+
import {HoistModel, LoadSpec, lookup, persistOptions, PlainObject, XH} from '@xh/hoist/core';
|
|
10
10
|
import {StoreRecord} from '@xh/hoist/data';
|
|
11
11
|
import {bindable, computed, makeObservable} from '@xh/hoist/mobx';
|
|
12
12
|
import {ReactNode} from 'react';
|
|
@@ -53,6 +53,10 @@ export class ClientDetailModel extends HoistModel implements ActivityDetailProvi
|
|
|
53
53
|
override onLinked() {
|
|
54
54
|
super.onLinked();
|
|
55
55
|
|
|
56
|
+
this.persistWith = persistOptions(this.clientsModel.persistWith, {
|
|
57
|
+
pathPrefix: 'activityGrid'
|
|
58
|
+
});
|
|
59
|
+
|
|
56
60
|
this.addReaction(
|
|
57
61
|
{
|
|
58
62
|
track: () => this.clientsModel.gridModel.selectedRecord,
|
|
@@ -10,7 +10,7 @@ import {fragment, hframe, vframe} from '@xh/hoist/cmp/layout';
|
|
|
10
10
|
import {creates, hoistCmp} from '@xh/hoist/core';
|
|
11
11
|
import {button, colChooserButton} from '@xh/hoist/desktop/cmp/button';
|
|
12
12
|
import {errorMessage} from '@xh/hoist/cmp/error';
|
|
13
|
-
import {
|
|
13
|
+
import {filterChooser} from '@xh/hoist/desktop/cmp/filter';
|
|
14
14
|
import {switchInput} from '@xh/hoist/desktop/cmp/input';
|
|
15
15
|
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
16
16
|
import {recordActionBar} from '@xh/hoist/desktop/cmp/record';
|
|
@@ -44,7 +44,7 @@ export const rolePanel = hoistCmp.factory({
|
|
|
44
44
|
selModel: gridModel.selModel
|
|
45
45
|
}),
|
|
46
46
|
'-',
|
|
47
|
-
|
|
47
|
+
filterChooser({popover: true, flex: 1}),
|
|
48
48
|
'-',
|
|
49
49
|
switchInput({
|
|
50
50
|
bind: 'showInGroups',
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {HoistModel} from '../core';
|
|
7
|
+
import {HoistModel, HoistRoute} from '../core';
|
|
8
8
|
import {action, observable, makeObservable} from '@xh/hoist/mobx';
|
|
9
9
|
import {mergeDeep} from '@xh/hoist/utils/js';
|
|
10
10
|
import {isOmitted} from '@xh/hoist/utils/impl';
|
|
@@ -38,11 +38,11 @@ export class RouterModel extends HoistModel {
|
|
|
38
38
|
/**
|
|
39
39
|
* Add routes to the router.
|
|
40
40
|
*
|
|
41
|
-
* @param routes - collection of
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* @param routes - collection of {@link HoistRoute} specs. In addition to the standard Router5
|
|
42
|
+
* route config, each spec supports an `omit` keyword to allow declarative exclusion.
|
|
43
|
+
* Otherwise these are Router5 configs to be passed directly to the Router5 API.
|
|
44
44
|
*/
|
|
45
|
-
addRoutes(routes:
|
|
45
|
+
addRoutes(routes: HoistRoute[]) {
|
|
46
46
|
this.router.add(this.preprocessRoutes(routes));
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -100,7 +100,7 @@ export class RouterModel extends HoistModel {
|
|
|
100
100
|
return ret;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
private preprocessRoutes(routes) {
|
|
103
|
+
private preprocessRoutes(routes: HoistRoute[]): HoistRoute[] {
|
|
104
104
|
const ret = routes.filter(r => !isOmitted(r));
|
|
105
105
|
ret.forEach(r => {
|
|
106
106
|
if (r.children) r.children = this.preprocessRoutes(r.children);
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { TabConfig, TabContainerModel } from '@xh/hoist/cmp/tab';
|
|
2
2
|
import { ViewManagerModel } from '@xh/hoist/cmp/viewmanager';
|
|
3
|
-
import { HoistAppModel, InitContext } from '@xh/hoist/core';
|
|
4
|
-
import { Route } from 'router5';
|
|
3
|
+
import { HoistAppModel, HoistRoute, InitContext } from '@xh/hoist/core';
|
|
5
4
|
export declare class AppModel extends HoistAppModel {
|
|
6
5
|
tabModel: TabContainerModel;
|
|
7
6
|
viewManagerModels: Record<string, ViewManagerModel>;
|
|
8
7
|
static get readonly(): boolean;
|
|
9
8
|
constructor();
|
|
10
9
|
initAsync(ctx: InitContext): Promise<void>;
|
|
11
|
-
getRoutes():
|
|
10
|
+
getRoutes(): HoistRoute[];
|
|
12
11
|
getAppMenuButtonExtraItems(): any[];
|
|
13
|
-
getTabRoutes():
|
|
12
|
+
getTabRoutes(): HoistRoute[];
|
|
14
13
|
createTabs(): TabConfig[];
|
|
15
14
|
/** Open the primary business-facing application, typically 'app'. */
|
|
16
15
|
openPrimaryApp(): void;
|
|
@@ -3,10 +3,20 @@ import { FilterChooserModel } from '@xh/hoist/cmp/filter';
|
|
|
3
3
|
import { FormModel } from '@xh/hoist/cmp/form';
|
|
4
4
|
import { ColumnSpec, GridModel } from '@xh/hoist/cmp/grid';
|
|
5
5
|
import { GroupingChooserModel } from '@xh/hoist/cmp/grouping';
|
|
6
|
-
import { HoistModel, LoadSpec, PlainObject } from '@xh/hoist/core';
|
|
6
|
+
import { HoistModel, LoadSpec, LocalDateUnit, PlainObject } from '@xh/hoist/core';
|
|
7
7
|
import { Cube } from '@xh/hoist/data';
|
|
8
8
|
import { LocalDate } from '@xh/hoist/utils/datetime';
|
|
9
9
|
import { ActivityDetailProvider } from './detail/ActivityDetailModel';
|
|
10
|
+
/**
|
|
11
|
+
* A relative time interval (e.g. 7 days) ending on the current app day. Drives the quick-select
|
|
12
|
+
* buttons and the persisted time period, which rolls forward to end on today on restore.
|
|
13
|
+
*/
|
|
14
|
+
export interface ActivityInterval {
|
|
15
|
+
value: number;
|
|
16
|
+
unit: LocalDateUnit;
|
|
17
|
+
}
|
|
18
|
+
/** Quick-select intervals offered in the toolbar - labels are derived as `${value}${unit[0]}`. */
|
|
19
|
+
export declare const INTERVALS: ActivityInterval[];
|
|
10
20
|
export declare class ActivityTrackingModel extends HoistModel implements ActivityDetailProvider {
|
|
11
21
|
telemetryPrefix: string;
|
|
12
22
|
/** FormModel for server-side querying controls. */
|
|
@@ -45,10 +55,11 @@ export declare class ActivityTrackingModel extends HoistModel implements Activit
|
|
|
45
55
|
setDataFields(dataFields: ActivityTrackingDataFieldSpec[]): void;
|
|
46
56
|
toggleFilterChooser(): void;
|
|
47
57
|
adjustDates(dir: 'add' | 'subtract'): void;
|
|
48
|
-
adjustStartDate(
|
|
49
|
-
isInterval(
|
|
58
|
+
adjustStartDate(interval: ActivityInterval): void;
|
|
59
|
+
isInterval(interval: ActivityInterval): boolean;
|
|
50
60
|
getDisplayName(fieldName: string): string;
|
|
51
61
|
private createQueryFormModel;
|
|
62
|
+
private setupPeriodPersistence;
|
|
52
63
|
private loadGridAsync;
|
|
53
64
|
private cubeLabelComparator;
|
|
54
65
|
private getComparableValForDim;
|
|
@@ -4,6 +4,9 @@ import { RecordActionSpec, StoreRecord } from '@xh/hoist/data';
|
|
|
4
4
|
import { BaseAdminTabModel } from '@xh/hoist/admin/tabs/BaseAdminTabModel';
|
|
5
5
|
export declare class ClientsModel extends BaseAdminTabModel {
|
|
6
6
|
telemetryPrefix: string;
|
|
7
|
+
persistWith: {
|
|
8
|
+
localStorageKey: string;
|
|
9
|
+
};
|
|
7
10
|
lastRefresh: number;
|
|
8
11
|
gridModel: GridModel;
|
|
9
12
|
groupBy: 'user' | 'instance';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HoistModel } from '../core';
|
|
1
|
+
import { HoistModel, HoistRoute } from '../core';
|
|
2
2
|
import { Router, State } from 'router5';
|
|
3
3
|
import 'router5-plugin-browser';
|
|
4
4
|
/**
|
|
@@ -19,11 +19,11 @@ export declare class RouterModel extends HoistModel {
|
|
|
19
19
|
/**
|
|
20
20
|
* Add routes to the router.
|
|
21
21
|
*
|
|
22
|
-
* @param routes - collection of
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* @param routes - collection of {@link HoistRoute} specs. In addition to the standard Router5
|
|
23
|
+
* route config, each spec supports an `omit` keyword to allow declarative exclusion.
|
|
24
|
+
* Otherwise these are Router5 configs to be passed directly to the Router5 API.
|
|
25
25
|
*/
|
|
26
|
-
addRoutes(routes:
|
|
26
|
+
addRoutes(routes: HoistRoute[]): void;
|
|
27
27
|
/**
|
|
28
28
|
* Add a routeName to the current route, preserving params
|
|
29
29
|
* @param routeName - the routeName to append
|
|
@@ -4,6 +4,7 @@ import { HoistModel, HoistProps, LayoutProps, PlainObject, ReactionSpec, TestSup
|
|
|
4
4
|
import { RecordSet } from '@xh/hoist/data/impl/RecordSet';
|
|
5
5
|
import type { ColDef, ColGroupDef, GetContextMenuItemsParams, GridOptions, GridReadyEvent, ProcessCellForExportParams } from '@xh/hoist/kit/ag-grid';
|
|
6
6
|
import { type MouseEvent } from 'react';
|
|
7
|
+
import { PartialDeep } from 'type-fest';
|
|
7
8
|
import './Grid.scss';
|
|
8
9
|
import { GridModel } from './GridModel';
|
|
9
10
|
export interface GridProps<M extends GridModel = GridModel> extends HoistProps<M>, LayoutProps, TestSupportProps {
|
|
@@ -16,7 +17,7 @@ export interface GridProps<M extends GridModel = GridModel> extends HoistProps<M
|
|
|
16
17
|
*
|
|
17
18
|
* Note that changes to these options after the component's initial render will be ignored.
|
|
18
19
|
*/
|
|
19
|
-
agOptions?: GridOptions
|
|
20
|
+
agOptions?: PartialDeep<GridOptions>;
|
|
20
21
|
/**
|
|
21
22
|
* Callback when the grid has initialized. The component will call this with the ag-Grid
|
|
22
23
|
* event after running its internal handler to associate the ag-Grid APIs with its model.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AgGridModel } from '@xh/hoist/cmp/ag-grid';
|
|
2
|
-
import { Column, ColumnGroup, ColumnOrGroup, ColumnOrGroupSpec, ColumnSpec, GridAutosizeMode, GridFilterModelConfig, GridGroupSortFn, TreeStyle } from '@xh/hoist/cmp/grid';
|
|
2
|
+
import { Column, ColumnGroup, ColumnOrGroup, ColumnOrGroupSpec, ColumnSpec, GridAutosizeMode, GridFilterModelConfig, GridGroupSortFn, IColChooserModel, TreeStyle } from '@xh/hoist/cmp/grid';
|
|
3
3
|
import { GridFilterModel } from '@xh/hoist/cmp/grid/filter/GridFilterModel';
|
|
4
4
|
import { Awaitable, HoistModel, HSide, LoadSpec, PlainObject, SizingMode, Some, TaskObserver, Thunkable, VSide } from '@xh/hoist/core';
|
|
5
5
|
import { Store, StoreConfig, StoreRecord, StoreRecordId, StoreRecordOrId, StoreSelectionConfig, StoreSelectionModel, StoreTransaction } from '@xh/hoist/data';
|
|
@@ -285,7 +285,7 @@ export declare class GridModel extends HoistModel {
|
|
|
285
285
|
store: Store;
|
|
286
286
|
selModel: StoreSelectionModel;
|
|
287
287
|
treeMode: boolean;
|
|
288
|
-
colChooserModel:
|
|
288
|
+
colChooserModel: IColChooserModel;
|
|
289
289
|
rowClassFn: RowClassFn;
|
|
290
290
|
rowClassRules: Record<string, RowClassRuleFn>;
|
|
291
291
|
contextMenu: GridContextMenuSpec;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HSide, PersistOptions, Some } from '@xh/hoist/core';
|
|
1
|
+
import type { HoistModel, HSide, PersistOptions, Some } from '@xh/hoist/core';
|
|
2
2
|
import type { FilterBindTarget, FilterMatchMode, FilterValueSource, Store, StoreRecord } from '@xh/hoist/data';
|
|
3
3
|
import type { CellClassParams, CustomCellEditorProps, HeaderClassParams, HeaderValueGetterParams, ICellRendererParams, IRowNode, ITooltipParams, RowClassParams, ValueSetterParams } from '@xh/hoist/kit/ag-grid';
|
|
4
4
|
import type { ReactElement, ReactNode } from 'react';
|
|
@@ -101,6 +101,13 @@ export interface GridFilterBindTarget extends FilterBindTarget, FilterValueSourc
|
|
|
101
101
|
* @returns the formatted value for display.
|
|
102
102
|
*/
|
|
103
103
|
export type GroupRowRenderer = (context: ICellRendererParams) => ReactNode;
|
|
104
|
+
/** Cross-platform interface for desktop and mobile ColChooserModels. */
|
|
105
|
+
export interface IColChooserModel extends HoistModel {
|
|
106
|
+
readonly gridModel: GridModel;
|
|
107
|
+
readonly isOpen: boolean;
|
|
108
|
+
open(): void;
|
|
109
|
+
close(): void;
|
|
110
|
+
}
|
|
104
111
|
/**
|
|
105
112
|
* Configuration for a {@link ColChooserModel} - the model backing the grid column chooser UI.
|
|
106
113
|
* Passed via the `colChooserModel` config on {@link GridConfig}, or set app-wide via
|
|
@@ -21,6 +21,12 @@ export interface SegmentedControlOption {
|
|
|
21
21
|
* `intent` default. Defaults to the control's `intent`.
|
|
22
22
|
*/
|
|
23
23
|
intent?: Intent;
|
|
24
|
+
/**
|
|
25
|
+
* Optional stable identifier emitted on this option's rendered button as `data-testid`, for
|
|
26
|
+
* use by E2E tests. If omitted and the control itself has a `testId`, one is auto-derived as
|
|
27
|
+
* `${controlTestId}-${value}`. If neither is set, no attribute is emitted.
|
|
28
|
+
*/
|
|
29
|
+
testId?: string;
|
|
24
30
|
}
|
|
25
31
|
/**
|
|
26
32
|
* Variant of SegmentedControlOption for representing a null/"no value" selection.
|
|
@@ -41,4 +47,10 @@ export interface SegmentedControlNullOption {
|
|
|
41
47
|
* `intent` default. Defaults to the control's `intent`.
|
|
42
48
|
*/
|
|
43
49
|
intent?: Intent;
|
|
50
|
+
/**
|
|
51
|
+
* Optional stable identifier emitted on this option's rendered button as `data-testid`, for
|
|
52
|
+
* use by E2E tests. If omitted and the control itself has a `testId`, one is auto-derived as
|
|
53
|
+
* `${controlTestId}-null`. If neither is set, no attribute is emitted.
|
|
54
|
+
*/
|
|
55
|
+
testId?: string;
|
|
44
56
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { AppOptionSpec, HoistModel, InitContext, Thunkable } from './';
|
|
2
|
-
import { Route } from 'router5';
|
|
1
|
+
import { AppOptionSpec, HoistModel, HoistRoute, InitContext, Thunkable } from './';
|
|
3
2
|
import { ReactNode } from 'react';
|
|
4
3
|
/**
|
|
5
4
|
* Specialized base class for defining the central model for a Hoist app as specified by its
|
|
@@ -40,7 +39,7 @@ export declare class HoistAppModel extends HoistModel {
|
|
|
40
39
|
/**
|
|
41
40
|
* Provide the initial set of Router5 Routes to be used by this application.
|
|
42
41
|
*/
|
|
43
|
-
getRoutes():
|
|
42
|
+
getRoutes(): HoistRoute[];
|
|
44
43
|
/**
|
|
45
44
|
* Provide a list of app-wide options to be displayed in the App's built-in Options
|
|
46
45
|
* dialog, accessible from the default AppBar menu when this method returns non-empty.
|
|
@@ -3,6 +3,7 @@ import { LocalDate } from '@xh/hoist/utils/datetime';
|
|
|
3
3
|
import { MomentInput } from 'moment';
|
|
4
4
|
import { Component, FunctionComponent, ReactElement } from 'react';
|
|
5
5
|
import { DebounceSettings } from 'lodash';
|
|
6
|
+
import { Route } from 'router5';
|
|
6
7
|
/** Values available for intents. */
|
|
7
8
|
export type Intent = 'primary' | 'success' | 'warning' | 'danger';
|
|
8
9
|
/** Values available for App Theme. */
|
|
@@ -42,6 +43,17 @@ export type DebounceSpec = number | (DebounceSettings & {
|
|
|
42
43
|
*/
|
|
43
44
|
export type Content = ReactElement | FunctionComponent | Component | ElementFactory | (() => ReactElement);
|
|
44
45
|
export type DateLike = Date | LocalDate | MomentInput;
|
|
46
|
+
/**
|
|
47
|
+
* A Router5 {@link Route} spec, extended with Hoist's `omit` support for declarative exclusion of
|
|
48
|
+
* routes at registration time (e.g. role-gated sections). Note `omit` is evaluated once when routes
|
|
49
|
+
* are added to the router during app startup - it is not reactive.
|
|
50
|
+
*
|
|
51
|
+
* @see HoistAppModel.getRoutes
|
|
52
|
+
*/
|
|
53
|
+
export interface HoistRoute extends Omit<Route, 'children'> {
|
|
54
|
+
omit?: Thunkable<boolean>;
|
|
55
|
+
children?: HoistRoute[];
|
|
56
|
+
}
|
|
45
57
|
/** Valid units for the {@link LocalDate} adjustment methods. */
|
|
46
58
|
export type LocalDateUnit = 'year' | 'years' | 'quarter' | 'quarters' | 'month' | 'months' | 'week' | 'weeks' | 'day' | 'days';
|
|
47
59
|
export type PageState =
|
|
@@ -24,6 +24,11 @@ export interface FilterChooserProps extends HoistProps<FilterChooserModel>, Layo
|
|
|
24
24
|
placeholder?: string;
|
|
25
25
|
/** Icon clicked to launch favorites menu. (Defaults to Icon.favorite()) */
|
|
26
26
|
favoritesIcon?: ReactElement;
|
|
27
|
+
/**
|
|
28
|
+
* True to render collapsed in-place, expanding into a popover when opened - useful in toolbars
|
|
29
|
+
* and other height-constrained containers. Opens in the direction set by `menuPlacement`.
|
|
30
|
+
*/
|
|
31
|
+
popover?: boolean;
|
|
27
32
|
}
|
|
28
33
|
/**
|
|
29
34
|
* A Select based control for searching and choosing filters.
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import '@xh/hoist/desktop/register';
|
|
2
|
-
import './PopoverFilterChooser.scss';
|
|
3
2
|
import { FilterChooserProps } from './FilterChooser';
|
|
4
3
|
/**
|
|
5
4
|
* A wrapper around a FilterChooser that renders in a popover when opened, allowing it to expand
|
|
6
5
|
* vertically beyond the height of a toolbar.
|
|
6
|
+
*
|
|
7
|
+
* @deprecated Use `filterChooser({popover: true})` instead - the popover behavior is now a built-in
|
|
8
|
+
* mode of `FilterChooser`. This alias will be removed in a future major release.
|
|
7
9
|
* @see FilterChooser
|
|
8
10
|
*/
|
|
9
11
|
export declare const PopoverFilterChooser: import("react").FC<FilterChooserProps>, popoverFilterChooser: import("@xh/hoist/core").ElementFactory<FilterChooserProps>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ColChooserConfig, GridModel } from '@xh/hoist/cmp/grid';
|
|
1
|
+
import { ColChooserConfig, GridModel, IColChooserModel } from '@xh/hoist/cmp/grid';
|
|
2
2
|
import { HoistModel } from '@xh/hoist/core';
|
|
3
3
|
import type { FilterMatchMode } from '@xh/hoist/data';
|
|
4
4
|
import { LeftRightChooserModel } from '@xh/hoist/desktop/cmp/leftrightchooser';
|
|
@@ -6,7 +6,7 @@ import { LeftRightChooserModel } from '@xh/hoist/desktop/cmp/leftrightchooser';
|
|
|
6
6
|
* State management for the ColChooser component.
|
|
7
7
|
* @internal
|
|
8
8
|
*/
|
|
9
|
-
export declare class ColChooserModel extends HoistModel {
|
|
9
|
+
export declare class ColChooserModel extends HoistModel implements IColChooserModel {
|
|
10
10
|
xhImpl: boolean;
|
|
11
11
|
gridModel: GridModel;
|
|
12
12
|
lrModel: LeftRightChooserModel;
|