@xh/hoist 86.2.0 → 86.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/.gitattributes +4 -0
- package/CHANGELOG.md +75 -0
- package/admin/AppModel.ts +38 -11
- 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/RoleModel.ts +11 -18
- package/admin/tabs/userData/roles/RolePanel.ts +2 -9
- package/appcontainer/RouterModel.ts +6 -6
- package/build/types/admin/AppModel.d.ts +7 -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/admin/tabs/userData/roles/RoleModel.d.ts +2 -2
- 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/data/Store.d.ts +3 -2
- 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/build/types/svc/PrefService.d.ts +12 -1
- package/build/types/utils/datetime/LocalDate.d.ts +23 -1
- 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/data/Store.ts +3 -2
- package/desktop/cmp/filechooser/FileChooser.ts +6 -3
- package/desktop/cmp/filechooser/FileChooserModel.ts +12 -5
- package/desktop/cmp/filter/FilterChooser.scss +42 -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/svc/PrefService.ts +80 -20
- package/svc/README.md +6 -0
- package/utils/datetime/LocalDate.ts +24 -1
- package/desktop/cmp/filter/PopoverFilterChooser.scss +0 -42
|
@@ -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 =
|
|
@@ -267,8 +267,9 @@ export declare class Store extends HoistBase implements FilterBindTarget, Filter
|
|
|
267
267
|
* Add new Records to this Store in a local, uncommitted state - i.e. with data that has yet to
|
|
268
268
|
* be persisted back to, or sourced from, the server or other data source of record.
|
|
269
269
|
*
|
|
270
|
-
* Note that data objects passed to this method must include a
|
|
271
|
-
*
|
|
270
|
+
* Note that data objects passed to this method must include a literal `id` property - this
|
|
271
|
+
* method does *not* run the Store's `idSpec` function. Callers can generate an id with
|
|
272
|
+
* `XH.genId()` if no natural ID can be produced locally on the client.
|
|
272
273
|
*
|
|
273
274
|
* For StoreRecord additions that originate from the server, call `updateData()` instead.
|
|
274
275
|
*
|
|
@@ -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;
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
import type { GridFilterFieldSpec } from '@xh/hoist/cmp/grid';
|
|
1
2
|
import { HoistModel } from '@xh/hoist/core';
|
|
2
3
|
import { FieldFilterOperator, FieldFilterSpec } from '@xh/hoist/data';
|
|
3
4
|
import { HeaderFilterModel } from '../HeaderFilterModel';
|
|
4
5
|
import { CustomTabModel } from './CustomTabModel';
|
|
5
6
|
type OperatorOptionValue = 'blank' | 'not blank' | FieldFilterOperator;
|
|
7
|
+
/**
|
|
8
|
+
* Whether a row for the given op renders the multi-value `select` input - the only custom-tab input
|
|
9
|
+
* that holds an array of values directly. The single-value inputs (number, date, text) are used
|
|
10
|
+
* otherwise, so array filter values bound for them must be expanded into one row each (see
|
|
11
|
+
* `CustomTabModel.doSyncWithFilter`). Mirrors the input choice made in `CustomRow`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function usesMultiValueInput(fieldSpec: GridFilterFieldSpec, op: FieldFilterOperator): boolean;
|
|
6
14
|
/**
|
|
7
15
|
* @internal
|
|
8
16
|
*/
|
|
@@ -14,7 +22,7 @@ export declare class CustomRowModel extends HoistModel {
|
|
|
14
22
|
inputVal: any;
|
|
15
23
|
/** FieldFilter config output of this row. */
|
|
16
24
|
get value(): FieldFilterSpec;
|
|
17
|
-
get fieldSpec():
|
|
25
|
+
get fieldSpec(): GridFilterFieldSpec;
|
|
18
26
|
get options(): {
|
|
19
27
|
label: string;
|
|
20
28
|
value: OperatorOptionValue;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HoistModel } from '@xh/hoist/core';
|
|
2
|
-
import { CompoundFilterOperator, FilterLike } from '@xh/hoist/data';
|
|
2
|
+
import { CompoundFilterOperator, FieldFilter, FilterLike } from '@xh/hoist/data';
|
|
3
3
|
import { HeaderFilterModel } from '../HeaderFilterModel';
|
|
4
4
|
import { CustomRowModel } from './CustomRowModel';
|
|
5
5
|
export declare class CustomTabModel extends HoistModel {
|
|
@@ -11,11 +11,17 @@ export declare class CustomTabModel extends HoistModel {
|
|
|
11
11
|
get filter(): FilterLike;
|
|
12
12
|
get fieldSpec(): import("../../../../../../../cmp/grid").GridFilterFieldSpec;
|
|
13
13
|
get currentGridFilter(): import("@xh/hoist/data").Filter;
|
|
14
|
-
get columnFilters():
|
|
14
|
+
get columnFilters(): FieldFilter[];
|
|
15
|
+
get columnCompoundFilter(): import("@xh/hoist/data").CompoundFilter;
|
|
15
16
|
constructor(headerFilterModel: HeaderFilterModel);
|
|
16
17
|
syncWithFilter(): void;
|
|
17
18
|
reset(): void;
|
|
18
19
|
addEmptyRow(): void;
|
|
19
20
|
removeRow(model: any): void;
|
|
20
21
|
private doSyncWithFilter;
|
|
22
|
+
private needsExpansion;
|
|
23
|
+
private mergeOpFor;
|
|
24
|
+
private deriveOp;
|
|
25
|
+
private get isRepresentable();
|
|
26
|
+
private collapseToArrayFilters;
|
|
21
27
|
}
|
|
@@ -127,6 +127,14 @@ export interface SelectProps extends HoistProps, HoistInputProps, LayoutProps {
|
|
|
127
127
|
selectOnFocus?: boolean;
|
|
128
128
|
/** Field on provided options for sourcing each option's value (default `value`). */
|
|
129
129
|
valueField?: string;
|
|
130
|
+
/**
|
|
131
|
+
* Function to generate a `SelectOption` for a (non-null) selected value not present in the
|
|
132
|
+
* current options list. Return null to fall back to the default value-as-label behavior.
|
|
133
|
+
*
|
|
134
|
+
* Useful with queryFn-based selects, readonly forms, or any case where options may not be
|
|
135
|
+
* loaded when a value is set, ensuring the value renders with its proper label.
|
|
136
|
+
*/
|
|
137
|
+
generateOptionFn?: (value: any) => SelectOption;
|
|
130
138
|
}
|
|
131
139
|
/**
|
|
132
140
|
* A managed wrapper around the React-Select combobox/dropdown component.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Dropzone from 'react-dropzone';
|
|
2
2
|
export { Dropzone };
|
|
3
|
+
export { ErrorCode } from 'react-dropzone';
|
|
3
4
|
export type { Accept, FileRejection, FileWithPath } from 'react-dropzone';
|
|
4
5
|
export declare const dropzone: import("@xh/hoist/core").ElementFactory<import("react-dropzone").DropzoneProps & import("react").RefAttributes<import("react-dropzone").DropzoneRef>>;
|
|
@@ -28,6 +28,7 @@ export declare const swiper: import("@xh/hoist/core").ElementFactory<import("rea
|
|
|
28
28
|
onScrollbarDragMove?: (swiper: import("swiper/types").Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void;
|
|
29
29
|
onScrollbarDragEnd?: (swiper: import("swiper/types").Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void;
|
|
30
30
|
onZoomChange?: (swiper: import("swiper/types").Swiper, scale: number, imageEl: HTMLElement, slideEl: HTMLElement) => void;
|
|
31
|
+
onVirtualUpdate?: (swiper: import("swiper/types").Swiper) => void;
|
|
31
32
|
onInit?: (swiper: import("swiper/types").Swiper) => any;
|
|
32
33
|
onBeforeDestroy?: (swiper: import("swiper/types").Swiper) => void;
|
|
33
34
|
onSlidesUpdated?: (swiper: import("swiper/types").Swiper) => void;
|
|
@@ -53,8 +54,8 @@ export declare const swiper: import("@xh/hoist/core").ElementFactory<import("rea
|
|
|
53
54
|
onReachEnd?: (swiper: import("swiper/types").Swiper) => void;
|
|
54
55
|
onToEdge?: (swiper: import("swiper/types").Swiper) => void;
|
|
55
56
|
onFromEdge?: (swiper: import("swiper/types").Swiper) => void;
|
|
56
|
-
onSetTranslate?: (swiper: import("swiper/types").Swiper, translate: number) => void;
|
|
57
|
-
onSetTransition?: (swiper: import("swiper/types").Swiper, transition: number) => void;
|
|
57
|
+
onSetTranslate?: (swiper: import("swiper/types").Swiper, translate: number, byController?: boolean | Swiper) => void;
|
|
58
|
+
onSetTransition?: (swiper: import("swiper/types").Swiper, transition: number, byController?: boolean | Swiper) => void;
|
|
58
59
|
onResize?: (swiper: import("swiper/types").Swiper) => void;
|
|
59
60
|
onObserverUpdate?: (swiper: import("swiper/types").Swiper) => void;
|
|
60
61
|
onBeforeLoopFix?: (swiper: import("swiper/types").Swiper) => void;
|
|
@@ -77,7 +78,7 @@ export declare const swiper: import("@xh/hoist/core").ElementFactory<import("rea
|
|
|
77
78
|
onBeforeInit?: (swiper: import("swiper/types").Swiper) => void;
|
|
78
79
|
onBeforeResize?: (swiper: import("swiper/types").Swiper) => void;
|
|
79
80
|
onBeforeSlideChangeStart?: (swiper: import("swiper/types").Swiper) => void;
|
|
80
|
-
onBeforeTransitionStart?: (swiper: import("swiper/types").Swiper, speed: number, internal:
|
|
81
|
+
onBeforeTransitionStart?: (swiper: import("swiper/types").Swiper, speed: number, internal: unknown) => void;
|
|
81
82
|
onChangeDirection?: (swiper: import("swiper/types").Swiper) => void;
|
|
82
83
|
onDoubleClick?: (swiper: import("swiper/types").Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void;
|
|
83
84
|
onDestroy?: (swiper: import("swiper/types").Swiper) => void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
1
|
+
import { GridModel, IColChooserModel } from '@xh/hoist/cmp/grid';
|
|
2
2
|
import { HoistModel, HSide } from '@xh/hoist/core';
|
|
3
3
|
import '@xh/hoist/mobile/register';
|
|
4
4
|
/**
|
|
@@ -7,7 +7,7 @@ import '@xh/hoist/mobile/register';
|
|
|
7
7
|
* It is not necessary to manually create instances of this class within an application.
|
|
8
8
|
* @internal
|
|
9
9
|
*/
|
|
10
|
-
export declare class ColChooserModel extends HoistModel {
|
|
10
|
+
export declare class ColChooserModel extends HoistModel implements IColChooserModel {
|
|
11
11
|
xhImpl: boolean;
|
|
12
12
|
gridModel: GridModel;
|
|
13
13
|
showRestoreDefaults: boolean;
|
|
@@ -109,6 +109,14 @@ export interface SelectProps extends HoistProps, HoistInputProps, LayoutProps {
|
|
|
109
109
|
title?: string;
|
|
110
110
|
/** Field on provided options for sourcing each option's value (default `value`). */
|
|
111
111
|
valueField?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Function to generate a `SelectOption` for a (non-null) selected value not present in the
|
|
114
|
+
* current options list. Return null to fall back to the default value-as-label behavior.
|
|
115
|
+
*
|
|
116
|
+
* Useful with queryFn-based selects, readonly forms, or any case where options may not be
|
|
117
|
+
* loaded when a value is set, ensuring the value renders with its proper label.
|
|
118
|
+
*/
|
|
119
|
+
generateOptionFn?: (value: any) => SelectOption;
|
|
112
120
|
}
|
|
113
121
|
/**
|
|
114
122
|
* A managed wrapper around the React-Select combobox/dropdown component.
|
|
@@ -25,6 +25,13 @@ export declare class PrefService extends HoistService {
|
|
|
25
25
|
* Check to see if a given preference has been *defined*.
|
|
26
26
|
*/
|
|
27
27
|
hasKey(key: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Check whether the current user has an explicit value on file for the given preference, vs.
|
|
30
|
+
* receiving the preference's server-side default value.
|
|
31
|
+
*
|
|
32
|
+
* @param key - unique key used to identify the pref.
|
|
33
|
+
*/
|
|
34
|
+
isSet(key: string): boolean;
|
|
28
35
|
/**
|
|
29
36
|
* Get the value for a given key, either the user-specific value (if set) or the default.
|
|
30
37
|
* Typically accessed via the convenience alias {@link XH.getPref}.
|
|
@@ -47,7 +54,10 @@ export declare class PrefService extends HoistService {
|
|
|
47
54
|
*/
|
|
48
55
|
set(key: string, value: any): void;
|
|
49
56
|
/**
|
|
50
|
-
* Restore a preference to its default value.
|
|
57
|
+
* Restore a preference to its default value, clearing the user's explicit value on the server.
|
|
58
|
+
*
|
|
59
|
+
* Unlike `set()`, this clears the user's explicit value rather than persisting the default as
|
|
60
|
+
* one - so {@link isSet} will report `false` afterwards. Saved asynchronously (see `set()`).
|
|
51
61
|
*/
|
|
52
62
|
unset(key: string): void;
|
|
53
63
|
/**
|
|
@@ -66,6 +76,7 @@ export declare class PrefService extends HoistService {
|
|
|
66
76
|
pushPendingAsync(): Promise<void>;
|
|
67
77
|
private pushPendingBuffered;
|
|
68
78
|
private loadPrefsAsync;
|
|
79
|
+
private ensureKeyExists;
|
|
69
80
|
private validateBeforeSet;
|
|
70
81
|
private valueIsOfType;
|
|
71
82
|
}
|
|
@@ -9,6 +9,15 @@ import { Moment, MomentInput } from 'moment';
|
|
|
9
9
|
* For efficiency and to enable strict equality checks, instances of this class are memoized:
|
|
10
10
|
* only a single version of the object will be created and returned for each calendar day,
|
|
11
11
|
* as long as the caller uses one of the *public factory methods*, which they always should!
|
|
12
|
+
*
|
|
13
|
+
* Instances serialize directly to their ISO date string (e.g. '2024-01-15') via built-in
|
|
14
|
+
* `toString()`, `valueOf()`, and `toJSON()` overrides. This means a LocalDate can be passed
|
|
15
|
+
* as-is within the params or body of a `FetchService` request (or any `JSON.stringify()` call)
|
|
16
|
+
* and will serialize as expected - prefer this over calling `toString()` or `format()` yourself.
|
|
17
|
+
*
|
|
18
|
+
* Instances also support natural comparison: because they are memoized, `===` tests whether two
|
|
19
|
+
* references are the same calendar day, while `valueOf()` returning the (lexically sortable) ISO
|
|
20
|
+
* string means the relational operators `<`, `>`, `<=`, `>=` order instances chronologically.
|
|
12
21
|
*/
|
|
13
22
|
export declare class LocalDate {
|
|
14
23
|
static readonly VALID_UNITS: Set<LocalDateUnit>;
|
|
@@ -33,7 +42,11 @@ export declare class LocalDate {
|
|
|
33
42
|
* @param val - any string, timestamp, or date parsable by moment.js.
|
|
34
43
|
*/
|
|
35
44
|
static from(val: MomentInput | LocalDate): LocalDate;
|
|
36
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* LocalDate representing the current day in the browser's local time zone.
|
|
47
|
+
* See `currentAppDay()` / `currentServerDay()` to resolve "today" in the app or server zone,
|
|
48
|
+
* which can differ from the browser for users in another region.
|
|
49
|
+
*/
|
|
37
50
|
static today(): LocalDate;
|
|
38
51
|
/** LocalDate representing the current day in the App TimeZone */
|
|
39
52
|
static currentAppDay(): LocalDate;
|
|
@@ -46,9 +59,17 @@ export declare class LocalDate {
|
|
|
46
59
|
/** Is the input value a local Date? */
|
|
47
60
|
static isLocalDate(val: any): boolean;
|
|
48
61
|
get isoString(): string;
|
|
62
|
+
/** JS `Date` for this day at midnight in the browser's local time zone. Fresh instance per call. */
|
|
49
63
|
get date(): Date;
|
|
64
|
+
/** A mutable moment.js clone - safe to modify without affecting this (immutable) instance. */
|
|
50
65
|
get moment(): Moment;
|
|
66
|
+
/** Epoch millis for this day at midnight in the browser's local time zone. */
|
|
51
67
|
get timestamp(): number;
|
|
68
|
+
/**
|
|
69
|
+
* Format this date using moment.js format tokens, primarily for display.
|
|
70
|
+
* Note: to send a LocalDate to the server, pass the instance directly rather than a formatted
|
|
71
|
+
* string - it serializes to an ISO date on its own (see class-level docs).
|
|
72
|
+
*/
|
|
52
73
|
format(...args: any[]): string;
|
|
53
74
|
dayOfWeek(): string;
|
|
54
75
|
get isToday(): boolean;
|
|
@@ -83,6 +104,7 @@ export declare class LocalDate {
|
|
|
83
104
|
currentOrNextWeekday(): LocalDate;
|
|
84
105
|
/** The same date if already a weekday, or the previous weekday. */
|
|
85
106
|
currentOrPreviousWeekday(): LocalDate;
|
|
107
|
+
/** Difference between this date and `other` in the given unit; positive when this is later. */
|
|
86
108
|
diff(other: LocalDate, unit?: LocalDateUnit): number;
|
|
87
109
|
/** @internal - use one of the static factory methods instead. */
|
|
88
110
|
private constructor();
|
package/cmp/grid/Grid.ts
CHANGED
|
@@ -49,6 +49,7 @@ import {composeRefs, createObservableRef, getLayoutProps} from '@xh/hoist/utils/
|
|
|
49
49
|
import classNames from 'classnames';
|
|
50
50
|
import {compact, debounce, isBoolean, isEmpty, isEqual, isNil, max, maxBy, merge} from 'lodash';
|
|
51
51
|
import {type MouseEvent} from 'react';
|
|
52
|
+
import {PartialDeep} from 'type-fest';
|
|
52
53
|
import './Grid.scss';
|
|
53
54
|
import {GridModel} from './GridModel';
|
|
54
55
|
import {columnGroupHeader} from './impl/ColumnGroupHeader';
|
|
@@ -66,7 +67,7 @@ export interface GridProps<M extends GridModel = GridModel>
|
|
|
66
67
|
*
|
|
67
68
|
* Note that changes to these options after the component's initial render will be ignored.
|
|
68
69
|
*/
|
|
69
|
-
agOptions?: GridOptions
|
|
70
|
+
agOptions?: PartialDeep<GridOptions>;
|
|
70
71
|
|
|
71
72
|
/**
|
|
72
73
|
* Callback when the grid has initialized. The component will call this with the ag-Grid
|
|
@@ -392,7 +393,14 @@ export class GridLocalModel extends HoistModel {
|
|
|
392
393
|
return {
|
|
393
394
|
track: () => [model.agApi, model.groupBy],
|
|
394
395
|
run: ([agApi, groupBy]) => {
|
|
395
|
-
if (agApi)
|
|
396
|
+
if (!agApi) return;
|
|
397
|
+
agApi.setRowGroupColumns(groupBy);
|
|
398
|
+
|
|
399
|
+
// Re-assert configured visibility - AG Grid re-shows a column when ungrouped (#4473).
|
|
400
|
+
const state = model.columnState
|
|
401
|
+
.filter(({colId}) => !groupBy.includes(colId))
|
|
402
|
+
.map(({colId, hidden}) => ({colId, hide: hidden}));
|
|
403
|
+
agApi.applyColumnState({state});
|
|
396
404
|
}
|
|
397
405
|
};
|
|
398
406
|
}
|
package/cmp/grid/GridModel.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
GridAutosizeMode,
|
|
17
17
|
GridFilterModelConfig,
|
|
18
18
|
GridGroupSortFn,
|
|
19
|
+
IColChooserModel,
|
|
19
20
|
isColumnSpec,
|
|
20
21
|
TreeStyle
|
|
21
22
|
} from '@xh/hoist/cmp/grid';
|
|
@@ -498,7 +499,7 @@ export class GridModel extends HoistModel {
|
|
|
498
499
|
store: Store;
|
|
499
500
|
selModel: StoreSelectionModel;
|
|
500
501
|
treeMode: boolean;
|
|
501
|
-
colChooserModel:
|
|
502
|
+
colChooserModel: IColChooserModel;
|
|
502
503
|
rowClassFn: RowClassFn;
|
|
503
504
|
rowClassRules: Record<string, RowClassRuleFn>;
|
|
504
505
|
contextMenu: GridContextMenuSpec;
|
|
@@ -1259,7 +1260,7 @@ export class GridModel extends HoistModel {
|
|
|
1259
1260
|
}
|
|
1260
1261
|
|
|
1261
1262
|
showColChooser() {
|
|
1262
|
-
|
|
1263
|
+
this.colChooserModel?.open();
|
|
1263
1264
|
}
|
|
1264
1265
|
|
|
1265
1266
|
noteAgColumnStateChanged(agColState: AgColumnState[]) {
|
|
@@ -1986,7 +1987,7 @@ export class GridModel extends HoistModel {
|
|
|
1986
1987
|
};
|
|
1987
1988
|
}
|
|
1988
1989
|
|
|
1989
|
-
private parseChooserModel(chooserModel: GridConfig['colChooserModel']):
|
|
1990
|
+
private parseChooserModel(chooserModel: GridConfig['colChooserModel']): IColChooserModel {
|
|
1990
1991
|
if (!chooserModel) return null;
|
|
1991
1992
|
|
|
1992
1993
|
const modelClass = XH.isMobileApp ? MobileColChooserModel : DesktopColChooserModel;
|
package/cmp/grid/Types.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {HSide, PersistOptions, Some} from '@xh/hoist/core';
|
|
8
|
+
import type {HoistModel, HSide, PersistOptions, Some} from '@xh/hoist/core';
|
|
9
9
|
import type {
|
|
10
10
|
FilterBindTarget,
|
|
11
11
|
FilterMatchMode,
|
|
@@ -142,6 +142,14 @@ export interface GridFilterBindTarget extends FilterBindTarget, FilterValueSourc
|
|
|
142
142
|
*/
|
|
143
143
|
export type GroupRowRenderer = (context: ICellRendererParams) => ReactNode;
|
|
144
144
|
|
|
145
|
+
/** Cross-platform interface for desktop and mobile ColChooserModels. */
|
|
146
|
+
export interface IColChooserModel extends HoistModel {
|
|
147
|
+
readonly gridModel: GridModel;
|
|
148
|
+
readonly isOpen: boolean;
|
|
149
|
+
open(): void;
|
|
150
|
+
close(): void;
|
|
151
|
+
}
|
|
152
|
+
|
|
145
153
|
/**
|
|
146
154
|
* Configuration for a {@link ColChooserModel} - the model backing the grid column chooser UI.
|
|
147
155
|
* Passed via the `colChooserModel` config on {@link GridConfig}, or set app-wide via
|
|
@@ -138,7 +138,7 @@ function replaceHoistToken(token: string, gridModel: GridModel): Some<RecordActi
|
|
|
138
138
|
text: 'Columns...',
|
|
139
139
|
icon: Icon.gridPanel(),
|
|
140
140
|
hidden: !gridModel?.colChooserModel,
|
|
141
|
-
actionFn: () =>
|
|
141
|
+
actionFn: () => gridModel.colChooserModel?.open()
|
|
142
142
|
});
|
|
143
143
|
case 'expandCollapseAll': // For backward compatibility
|
|
144
144
|
case 'expandCollapse':
|
|
@@ -33,6 +33,13 @@ export interface SegmentedControlOption {
|
|
|
33
33
|
* `intent` default. Defaults to the control's `intent`.
|
|
34
34
|
*/
|
|
35
35
|
intent?: Intent;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Optional stable identifier emitted on this option's rendered button as `data-testid`, for
|
|
39
|
+
* use by E2E tests. If omitted and the control itself has a `testId`, one is auto-derived as
|
|
40
|
+
* `${controlTestId}-${value}`. If neither is set, no attribute is emitted.
|
|
41
|
+
*/
|
|
42
|
+
testId?: string;
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
/**
|
|
@@ -58,4 +65,11 @@ export interface SegmentedControlNullOption {
|
|
|
58
65
|
* `intent` default. Defaults to the control's `intent`.
|
|
59
66
|
*/
|
|
60
67
|
intent?: Intent;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Optional stable identifier emitted on this option's rendered button as `data-testid`, for
|
|
71
|
+
* use by E2E tests. If omitted and the control itself has a `testId`, one is auto-derived as
|
|
72
|
+
* `${controlTestId}-null`. If neither is set, no attribute is emitted.
|
|
73
|
+
*/
|
|
74
|
+
testId?: string;
|
|
61
75
|
}
|
|
@@ -66,7 +66,7 @@ export class StoreFilterFieldImplModel extends HoistModel {
|
|
|
66
66
|
|
|
67
67
|
this.addReaction(
|
|
68
68
|
{
|
|
69
|
-
track: () => [this.filterText, gridModel?.columns
|
|
69
|
+
track: () => [this.filterText, gridModel?.columns],
|
|
70
70
|
run: () => this.regenerateFilter(),
|
|
71
71
|
fireImmediately: true
|
|
72
72
|
},
|
|
@@ -161,8 +161,7 @@ export class StoreFilterFieldImplModel extends HoistModel {
|
|
|
161
161
|
if (excludeFields) ret = without(ret, ...excludeFields);
|
|
162
162
|
|
|
163
163
|
if (gridModel) {
|
|
164
|
-
const
|
|
165
|
-
visibleCols = gridModel.getVisibleLeafColumns();
|
|
164
|
+
const visibleCols = gridModel.getVisibleLeafColumns();
|
|
166
165
|
|
|
167
166
|
// Push on dot-delimited grid column fields. These are supported by Grid and traverse
|
|
168
167
|
// sub-objects in StoreRecord.data to display nested properties. Given that Grid treats these
|
|
@@ -182,12 +181,12 @@ export class StoreFilterFieldImplModel extends HoistModel {
|
|
|
182
181
|
// Run exclude once more to support explicitly excluding a dot-sep field added above.
|
|
183
182
|
if (excludeFields) ret = without(ret, ...excludeFields);
|
|
184
183
|
|
|
185
|
-
// Final filter for column visibility, or explicit request for inclusion.
|
|
184
|
+
// Final filter for column visibility, or explicit request for inclusion. Deliberately
|
|
185
|
+
// not keyed to groupBy, so filter results stay stable across regrouping (see #4070).
|
|
186
186
|
ret = ret.filter(f => {
|
|
187
187
|
return (
|
|
188
188
|
(includeFields && includeFields.includes(f)) ||
|
|
189
|
-
visibleCols.find(c => c.field === f)
|
|
190
|
-
groupBy.includes(f)
|
|
189
|
+
visibleCols.find(c => c.field === f)
|
|
191
190
|
);
|
|
192
191
|
});
|
|
193
192
|
}
|
package/core/HoistAppModel.ts
CHANGED
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* Copyright © 2026 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {webSocketIndicator} from '@xh/hoist/cmp/websocket';
|
|
8
|
-
import {AppOptionSpec, HoistModel, InitContext, Thunkable} from './';
|
|
9
|
-
import {Route} from 'router5';
|
|
8
|
+
import {AppOptionSpec, HoistModel, HoistRoute, InitContext, Thunkable} from './';
|
|
10
9
|
import {ReactNode} from 'react';
|
|
11
10
|
/**
|
|
12
11
|
* Specialized base class for defining the central model for a Hoist app as specified by its
|
|
@@ -52,7 +51,7 @@ export class HoistAppModel extends HoistModel {
|
|
|
52
51
|
/**
|
|
53
52
|
* Provide the initial set of Router5 Routes to be used by this application.
|
|
54
53
|
*/
|
|
55
|
-
getRoutes():
|
|
54
|
+
getRoutes(): HoistRoute[] {
|
|
56
55
|
return [];
|
|
57
56
|
}
|
|
58
57
|
|